大多数浏览器和
Developer App 均支持流媒体播放。
-
认识用于空间计算的 SwiftUI
与我们一起游览太阳系,探索适用于 visionOS 的 SwiftUI!探索如何使用 Windows, Volume 和 Space 构建一个全新的 App 世界。我们将向您展示如何在此平台上开始使用 SwiftUI,同时,我们会构建一个天文学 App,添加 3D 内容,并构建完全沉浸式体验,将人们带到星星上。
章节
- 0:00 - Introduction
- 5:29 - Windows
- 14:06 - Volumes
- 20:35 - Full Spaces
资源
相关视频
WWDC23
-
下载
♪ 悦耳的器乐嘻哈 ♪ ♪ 大家好,欢迎收看 “认识用于空间计算的 SwiftUI”。 我是 SwiftUI 团队的 工程师 Andrew。 空间计算对我们而言, 非常振奋人心。 它为构建突破性的 App 开辟了一个充满新可能的世界。 在这次演讲中,我将带您了解 为该平台创建您自己的 优秀 App 时需要知道什么 以及 SwiftUI 中强大的 实现这一切的新功能。 当您要为空间计算构建 App 时, 最好的方式是使用 SwiftUI。 我们正在将 SwiftUI 引入一个充满未知的新未来。 它拥有全新的 3D 能力 比如 Volume、 Full Space 沉浸式体验、 全新的 3D 手势、效果和布局、 以及 RealityKit 深度集成, 还有很多其他功能。 这些功能只存在于 SwiftUI 中。 如果您 App 的代码已经就绪, 那它们大多数仍可在该平台运行。 对于新的代码 最好的方式是用 SwiftUI 来写。 我们非常信任 SwiftUI, 因此我们用 SwiftUI 从头 开始构建了这个系统。 从 App 的核心模块 比如按钮、开关、 TabView 和核心元素, 比如主页视图和控制中心, 到视频和 Safari 浏览器 等为人熟知的 App, 再到全新的体验 比如无边界的 3D 白板, Keynote 讲演的沉浸式排练, SwiftUI 支持所有这些内容 以及更多其他内容。 通过 SwiftUI,您只需 描述您的 App 界面, 系统会为您智能地提供默认值。 所以您只需要学习一次这些概念, 就可以将它们应用到任何地方。 在全新的平台上这将更加有用。 您现有的 SwiftUI 知识可以无缝 衔接到全新的外观和感受上。 来看一下按钮, 该系统上的按钮 和在其他平台上 您熟知和喜爱的按钮有很多相同之处。 比如 macOS 上的按钮。 该平台上的按钮 默认使用有边框的样式。 但按钮也有一些关键区别 以适应平台的习惯用法。 我们放大模拟器来仔细看看, 像这样的带边框的按钮 使用了醒目的材料背景, 所有按钮都有丰富的 悬停效果 可对您的眼睛、 手和指针输入做出反应。 它们还可以缩小, 并在按下时提供声音反馈。 当用户查看 在导航栏等位置的按钮时, 它们可以自动显示 您的按钮的标签信息。 来看一下 TabView, 它在您的 App 一侧, 提供快速和简洁的导航功能, 但不会干扰 App 的内容。 在查看时,展开 TabView 可以显示更多内容, 比如各个选项卡的标签。 TabView 可以让您更好地了解 SwiftUI 是如何 利用该平台独特之处的。 比如让您的 App 界面 对您查看的地方做出反应。 我们将这种体贴的设计 应用到了 App 的所有核心模块上。 从导航和演示 到控件和交互。 我们提供了这些智能平台的默认值, 这样您的 App 从开始就可以适应, 让您可以专注于打造出色的 App。 不止如此, SwiftUI 还包括了一整套全新的 专为 3D 环境打造的 API, 这些 API 从场景开始。 回想一下,SwiftUI 中的 App 是由场景组成的, 而场景是由视图组成的, 场景构成了您的 App 的顶级入口点, 比如 用于展示一个 或多个窗口的 WindowGroup。 在空间计算中构成 App 的 有三种场景:Window 、 Volume 和 Full Space。 Window 非常适合构建传统 和熟悉的界面。 包括丰富完整的 App, 如 Safari 浏览器或 Freeform, 到作为入口点的菜单, 再到更沉浸式的体验 如 Mindfulness。 Volume 是全新的 3D Window 样式, 是为了在在有限的空间中 展示物体或经历而专门建造的。 Volume 可以和其他 App 一起展示, 使之成为构建 轻量 3D 体验的优秀工具, 从使用 Quick Look 预览 3D 模型, 到使用 FaceTime 通话 与您的好友玩桌游。 最后是 Full Space, 一种构建 丰富沉浸式 App 的全新方式。 Full Space 让您可以 完全控制您的 App, 对其他 App 隐藏 Window, 让您可以把您的内容放在任何地方。 Full Space 在让人们 沉浸在周围的环境中的同时, 还可以增强现实, 或者可以让人们完全沉浸在 令人难以置信的新体验中。 从 Keynote 讲演中真实的排演 到令人兴奋的新型游戏, Full Space 开启了 一个充满新可能的世界。 这些场景类型可以一起使用, 每一种都是 为不同的情况专门构建的, 您可以用任何 对您的 App 有意义的方式 混合或匹配使用它们。 您可以通过 Window 展示 Volume, 比如展示建筑模型的 轻量 3D 预览。 您也可以通过 Volume 展示 Full Space, 让人们沉浸在这种模式中。 您还可以使用多种相同类型的场景, 这是将 App 分成不同部分的好办法, 人们可以分别打开或关闭 App。 让我们深入了解一下这些场景类型, 从 Window 开始。 我喜欢天文学, 并一直 在和志同道合的伙伴们 开发一款 App, 旨在帮助其他人 更好地了解地球和太阳系。 现在已经有了一个良好的开端, 他们在 Window 上构建了 这个华丽的介绍页。 但我有更长远的计划, 为了展示我们的 Window, 我使用了在其他平台上通用的 相同的 WindowGroup API。 使用 WindowGroup, 我还自动支持构建了多个 Window, 就像在 macOS 或 iPadOS 上一样。 我非常想给这个 App 添加一个新板块: 一个收集太阳系和宇宙 各种趣味知识的图书馆, 以便我的天文爱好者同学继续学习。 不过我一直纠结 要在 App 的哪里 添加这个功能板块。 为了回答这个问题, 我们来把 Window 的结构 分解成模块。 系统上的 Window 以漂亮的玻璃背景呈现, 通过显示周围的环境 让 App 内容易于查看, 使人们沉浸其中。 在我的 Window 中,我可以 使用我在其他平台已经熟悉的 相同的导航容器, 比如我可以使用 TabView 把我的 App 组织成一些顶级组件, 并让它们在我的 Window 前沿显示。 我还可以使用导航堆栈和拆分视图 组织 App 的层次结构, 并遵循与 iPadOS 类似的结构 显示丰富的信息。 列表是组织信息的另一个强大工具, 样式漂亮 正好合适。 当然我可以利用内置控件 如按钮、开关和选择器 给 App 添加交互性。 我认为这个图书馆视图 是TabView 的一个 非常好用的功能板块。 它让我的 App 顶级入口点 随时可以访问。 想要了解更深入的知识, 只需轻点一下。 为此我将内容包含在 TabView 中, 并为每个选项 提供一个带有标签的标签项。 看起来不错! TabView 外观独特, 悬挂在窗口边缘, 是我们添加到 SwiftUI 中的 新概念代表:挂饰。 挂饰可以在您 App 的 Window 上 放置附属视图, 甚至可以延伸到 Window 之外。 它们非常适合显示额外的 不属于 Window 本身的控件, 还可以避免干扰您的 App 内容。 我们还可以使用新的挂饰修改器 创建自己的挂饰。 来看一下我在做的 图书馆详细信息视图。 我已经开始做这一页了, 这一页分为几部分:一些摘要文本、 一个有趣的数据网格 和一个水平滚动的趣味知识列表。 但这些部分又少了一些东西, 为了找出问题,我们仔细看一下 App 的材料。 默认情况下,Window 的背景是一块 漂亮的新玻璃, 玻璃的设计是为了 和新的醒目材料融合, 这有助于为您的 App 提供视觉层次结构。 玻璃能够自动适应环境, 无论用在哪里都能保持内容清晰, 让您的 App 在任何环境中都非常好看。 也就是说,在这个平台中, 您的 App 没有深色或浅色外观, 材料会负责最难的部分。 让我们看看如何使用材料 让我的图书馆标签适合并更加清晰。 从数据网格开始, 目前,我的数据网格部分 只是由一个有标题的 VStack 和网格本身组成的。 让我们试着给它添加一个材料背景, 让这张页卡非常突出。 为此我使用了常规材料, 它给我们的 Window 玻璃上 添加了明亮的深色背景,并决定了 我的页卡形状, 提升了细节文本的清晰度。 我使用一个圆角矩形 并做了一些填充, 让这张页卡看起来很舒服。 我还会用同样的方法 处理趣味知识页卡。 这样看起来好多了! 接下来我们来看看趣味知识页卡。 放大仔细看看, 这张页卡是一个按钮, 当我点击它时, 就会显示趣味知识的更多细节。 我使用了自定义样式按钮, 可以提供很好的材料背景。 按钮的内容由标题、 细节正文和脚注构成,邀请人们 点击以了解更多。 文本“了解更多” 看起来有点太笨重了。 为了解决这一点,我会在文本上 使用次要前景样式。 注意,这个次要前景样式 会自动适应我们的背景, 使用一种漂亮、醒目的处理, 赋予内容以更大的视觉重量, 同时保持了次要文本的清晰度。 这些分层形状样式 非常适合表达相对视觉重量。 让 SwiftUI 根据环境 自动调整特定的材料。 在您的 App 中使用这些 和其他丰富的形状样式, 以及材料 API 可以在所有环境和平台中 创建出外观漂亮的界面。 接下来让我们转向交互。 借助空间计算, 我们可以使用 全新的方式与 App 交互。 最常见的交互形式 是简单地查看一个元素, 使用间接的捏合手势 来执行点击命令。 您还可以直接通过伸手和触摸 用手与 App 进行交互。 通过连接的触控板或手势, 您可以使用指针进行精确的输入。 该系统还可以连接硬件键盘使用, 借助键盘快捷键、键盘焦点 和键值修改器提高 App 效率。 最后,该系统支持您已经在其它平台 熟悉的的出色辅助功能技术, 如旁白和切换控制。 最棒的一点是 SwiftUI 控件 开箱即用, 帮助您完成许多繁重工作。 可以让所有人 在使用您的 App 时, 获得同样的体验。 您已经熟悉的手势 在 SwiftUI 中同样运行出色, 可以自动适应每种形式的交互。 如点击手势和拖拽手势。 我们还添加了一些全新的手势 以实现全新的丰富的 3D 交互, 比如 3D 旋转手势, 使用双手或连接的触控板 在三个维度上旋转视图。 当然了, 和其他平台一样的辅助功能 API 和技术也都可以出色运行, 比如旁白、转子、 动态类型和反转颜色。 其中很多功能 都为针对个平台进行了重新设计。 比如停留控制,它可以让人们 只用眼睛就能使用 并浏览您的 App。 想要更多地了解这些强大功能, 以及如何让所有人 都能访问您的 App , 请查看“创造易接近的空间体验”。 在空间计算中, 悬停(Hover)效果是一种重要的工具, 能使交互变得轻松直观。 比如只需查看交互式视图, 系统就会在视图上 显示微弱的高亮效果, 以提供您可以与之交互的反馈。 这些效果会自动适应 每种输入形式,让人们对自己 正在与之互动的内容充满信心。 悬停效果不仅可以提供快速反馈, 还可以帮助定位。 悬停效果是让您的 App 对人们眼睛注视的地方 做出反应的唯一方法。 为了尊重用户隐私,这些效果会 应用于 App 流程之外。 悬停效果会 自动添加到大多数控件中, 比如按钮、开关和文本框。 如果您使用 SwiftUI 提供的内置样式, 您会在 App 中 自动获得这些效果。 如果您使用自定义控件或样式, 请确保添加悬停效果 让用户觉得有响应, 使用简单。 让我们回到 App 中的趣味知识页卡 查看悬停效果在这里是如何运作的。 这些页卡看起来不错, 但有一个大问题。 因为我使用了自定义按钮样式, 所以我得自己为它添加悬停效果。 没有悬停效果就没有反馈表明 这些页卡可以进行交互。 好消息是这一点很容易解决。 我的按钮样式现在只添加了一些填充 和按钮标签的自定义的材料背景。 为了解决缺少的悬停反馈, 我会添加悬停效果修改器 到我的按钮样式中, 它自动为我选择了一个效果, 在背景中非常合适。 在这个例子中, 我们将了解高光效果。 仔细看看, 当我查看这些页卡按钮时, 我看到了微弱的高光效果, 这表明它们是可以互动的。 该效果甚至还能自动匹配 按钮背景的形状, 确保没人会错过 了解趣味知识的机会。 关于制作优秀的窗口 App, 我还有很多内容需要介绍, 想要了解更多 请查看 “为空间计算提升您的窗口 App”。 您会了解到如何升级多平台 App, 如何添加挂饰 并深入了解悬停效果、 材料和更多内容。 接下来让我们了解 App 的另一个维度 Volume。 我想帮助我的外星空间观测者伙伴 从一个新的视角来看待 我们称之为家的星球。 Volume 非常适合这项工作。 为了给我的 App 添加 Volume, 我将使用和主窗口 相同的 WindowGroup 场景, 简单地指定一个容积窗口样式。 我还可以为窗口 提供一个默认 3D 尺寸, 以适应我们的内容。 让我们看看模拟器中的 Volume。 在我的 Volume 内容中, 我使用了 RealityKit 中 全新的 Model3D API 来展示我们的设计师 放在一起的 3D 地球模型。 使用 Model3D 在我的 App 中 添加 3D 内容就这么简单。 Model3D 和 Image 一样, 能让加载和显示 精美的 3D 内容变得非常简单。 和 Image 不同的是 Model3D 加载是非同步的, 因为 3D 内容需要时间 来加载和准备显示。 和 AsyncImage 视图类似, 在我们等待内容加载的同时, Model3D 可以自动显示占位符视图。 我也可以完全控制 并显示自己的占位符。 值得注意的是, Model3D 就是另一种 形式的 SwiftUI 视图, 将 3D 引入 您的 SwiftUI App 中, 建立在您熟悉的概念之上, 自然延伸到布局系统、 视觉效果、手势等功能上。 让我们以这个为例, 看一下如何去做。 我想添加一些控件 让这个地球模型更有生机, 我已经构建了控制面板 UI, 并想把它放到我的地球模型前面。 为此我可以简单地 使用 ZStack 布局, ZStack 这样的布局可以自动了解 您的内容深度, 就像它们可以 自动了解宽度和高度一样。 默认情况下, Model3D 内容 会贴合全部三个维度的尺寸, 就像 2D 中的 Image 一样。 事实上,整个布局系统了解 您的内容深度和可用空间, 并且会相应地调整布局。 甚至新的修改器还会控制深度 随 App 布局的变化。 比如全新的填充 3D 修改器 可以在 SwiftUI 视图 和它们的正面或背面之间添加间隔, 在这个间隔中我们可以 添加控件。 请记住 Volume 的 设计能让您从任何角度 对其进行查看。 所以思考您的 App 内容 三个维度的安排 至关重要。 最后,为了让我的控件 看起来很棒而且接地气, 我要使用全新的 glassBackgroundEffect 修改器。 和我们之前在 标准 Window 中一样, 对它进行的漂亮的玻璃处理。 现在这些控件看起来已经差不多了。 我还想给 3D 地球模型 添加一个内容, 就是使用简单的方法 把地球模型转到任意地方, 这是我计划下一次 旅行目的地最喜欢用的方法。 为此我要给 Model3D 添加一个 3D 旋转效果, 在这个例子中是沿 Y 轴旋转。 我要使用一个状态变量 追踪要使用的旋转, 然后添加一个点击手势,启动后 我要使用一个全新的弹性弹簧动画 随机改变旋转角度。 我们来旋转一下。 好,西半球。 我的暑假安排真的跃然纸上了! 注意我们在地球模型上 应用的旋转效果 是真正的 3D 效果。 我们已经升级了这些 您已经知道如何使用的几何效果, 它们有全新的 3D 能力, 包括缩放、偏移 和自定义 3D 变换。 这已经是一个查看 地球模型很棒的方式了。 不过,我认为我们可以做得更好。 为此我会使用 RealityView, 这是一种全新的 SwiftUI 视图,可以让您 轻松使用 RealityKit 的全部功能。 使用 RealityView时, 我提供了一个闭包 来加载和制作 我的 RealityKit 内容。 为了显示我的地球模型, 我会创建一个 ModelEntity 并等待它加载, 加载完成后, 我会把它添加到 我的 RealityView 内容中来显示它。 注意,我可以直接在 我的 RealityView 闭包中 使用异步等待。 就像 Model3D 一样, 在我的内容加载完成之前, 它会自动显示一个占位符。 现在地球模型已经加载完成了。 我可以访问 丰富的 RealityKit API 库 为我的内容增添色彩。 在这个例子中,我要给 地球模型添加一些灯光, 让它看起来更阳光明媚一些。 我写了一些 RealityKit 代码, 来添加特别的基于图像的灯光, 我会在这里调用它。 现在它看起来令人愉快! 关于 RealityView 还有很多的内容要了解。 使用 RealityView 让融合用户界面 和丰富的 3D 体验 变得从未如此简单。 通过 RealityKit,您可以 添加各种丰富的行为, 比如自定义材料、自定义着色器 以及物理和复杂的动画。 要了解更多关于 RealityView 和 RealityKit 的信息, 我们准备了几场更深入的解说。 如果您准备好了,请查看 “使用 RealityKit 构建空间体验”。 现在让我们重点看一下 RealityView 的两个很酷的功能: 手势和附件。 使用 RealityView、 SwiftUI 手势可以自动运行, 把您的 3D 内容变得有生机, 这将是最简单的一种方式。 我想在 之前添加的点击手势上进行扩展, 根据我所点击的位置 在地球模型上放置一个标记, 这样我就能准确知道 下一次旅行的目的地了。 为此我会使用 SpatialTapGesture, 它让我可以全方位点击 3D 位置。 为了识别 我在地球模型上点击的位置, 我会使用全新的 targetedToAnyEntity 手势修改器。 它会为我提供所需的背景, 比如我点击的实体 以及实体上的相对位置。 我将使用它来寻找我的别针位置。 为了显示地球模型上的别针, 我可以使用 RealityView 附件, 附件非常适合将 自定义 SwiftUI 视图 与 RealityKit 实体内联在一起。 我可以直接在 RealityView 视图 附件闭包中添加 另外的 SwiftUI 视图, 让它们可以作为 RealityKit 实体 放置在我的 RealityView 中的任何位置。 我要在这里 添加一个别针附件并给它一个 识别标签, 然后在更新的闭包中, 我会查找我的附件实体, 并将其添加到 我的 RealityView 内容中来显示它, 然后我会放置 该实体来匹配点击位置。 让我们再旋转一次! 看来我得去收拾行李了! 我们对 有可能构建精彩的体积式 App 非常兴奋。 所以我们准备了一场演讲 来帮助您在从另一个 维度理解 SwiftUI App。 您会了解更多关于 结合使用 SwiftUI 和 RealityKit 构建 丰富的 3D 交互 和增加布局深度等内容。 最后,让我们借助 Full Space, 一种全新的构建 丰富沉浸式 3D 体验的方式, 让这款 App 超越 Window 。 借助 Full Space 您可以获得完全控制。 您可以在环境中随处放置内容, 以创造性的新方式 增强人们的环境现实。 或者您可以隐藏 周围环境让人们沉浸其中, 以创造令人惊叹的新体验。 让我们进入空间, 让太阳系栩栩如生。 我已经在主窗口的这一页上 开启了良好的第一步, 让我们的空想宇航员 可以亲眼观察外太空。 我只需要构建我的空间, 连接这个按钮来进行发射。 为了给我的 App 添加 Full Space, 我只需添加 一个 ImmersiveSpace 场景, 就像在 WindowGroup 中一样。 在它的主体中,我会为空间内容 提供一个根视图, 我还要为我的空间提供一个 ID, 以便能通过编程从主窗口打开它。 为了打开这个空间,我会使用全新的 openImmersiveSpace 环境动作, 在我的按钮中 我要调用该动作 并输入我们的空间 ID。 就像这样…… 发射成功! 这是一个很好的开始, 我可以以一种前所未有的方式 接近地球。 我可以欣赏所有丰富的细节, 比如那些逼真的云。 但这里少了些什么, 我想完全沉浸在这个空间里。 为此我将使用强大的空间工具: 沉浸样式。 一个 Full Space 有几种沉浸样式, 您可以随时 在这些样式之间进行转换。 借助混合沉浸,您的空间内容 可以和现实世界共存。 基于此,它非常适合轻量级体验 并增强人们的环境现实。 借助完全沉浸, 您的 App 可以变得完全沉浸 并隐藏人们的环境, 把人们带到一个令人惊叹的新世界。 渐进式沉浸是一个不错的中间选择, 让人们可以沉浸在 周围的现实世界里。 借助渐进式沉浸, 人们也可以使用设备上的数码表冠 来准确调整适合自己的沉浸程度。 我认为完全沉浸的空间非常适合 让我的 App 真正地脱离现实世界。 为此 我要回到我的 ImmersiveSpace 场景, 添加具备完全沉浸样式的 新 immersionStyle 修改器。 这里,我提供了一个支持的样式列表 和一个当前选择, 让我可以随时改变样式。 当我使用完全沉浸时, 系统会完全隐藏 人们周围真实世界的环境。 所以,我需要提供一个虚拟环境 让使用我的 App 的人沉浸其中。 当然了,外太空本身就是 最好的环境了。 通过创建一个新的 RealityView 来显示星空, 我们就能在环境中摘到星星了。 在 RealityView 的闭包中, 我会加载星空实体,准备好之后, 我会把它添加到 RealityView 的内容中。 然后我会把星空视图 添加到太阳系主体中, 就在地球和太阳旁边。 让我们放大来仔细看看,
真感觉自己是个追星星的人呢。
为了让我的空间更上一层楼, 我可以使用 ARKit, 一个与系统深度集成的 强大框架,提供 对人们现实世界环境的 丰富实时理解。 通过世界跟踪和场景理解等 API, 您可以将内容 放置在现实世界的表面上。 还有手部跟踪,一种神奇的新工具 您可以使用它构建自定义手势, 使用真实物理世界与您的内容互动, 等等。 想了解更多关于 如何在您的空间使用 ARKit, 请查看“认识用于空间计算的 ARKit”。 我的 App 可能会解锁 ARKit 功能, 为此,我感到非常兴奋, 想让您先睹为快, 看一看我一直在做的一项功能。 通过将 ARKit 整合到我的空间中, 我可以实施一个新手势 召唤地球, 所以您可以把世界掌握在手中。 对于您可以利用 Full Space 做什么, 我们只是展示了冰山一角。 我们还准备了另一个演讲 以助您完成”太空发射”, 请查看“使用 SwiftUI 超越 Window”, 以更深入地了解空间的基本原理, 了解给环境添加效果、 显示虚拟手、 与同播共享整合等高级工具。 如果您想要完全控制 在空间中呈现内容, 那么您可以使用 Metal 和全新的 CompositorServices 框架, 请查看“探索用于 沉浸式 App 的 Metal”以了解更多。 我们迫不及待地期待您使用 SwiftUI 构建精彩的新 App 了。 从漂亮的 Window, 到 Volume 的全新维度, 再到 Full Space 惊人的沉浸式体验, 一个充满可能性的世界 正在等着您去探索。 如果您准备好深入了解, 我们为您准备了更多精彩的演讲, 比如“空间设计的原则”, 您可以了解更多关于如何设计 出彩的 App 的内容。 如果您想把现有的 UIKit 代码放到平台上, 请查看 “认识用于空间计算的 UIKit”。 感谢您的观看,希望您能够 收获一段空间计算的愉快时光! ♪
-
-
2:02 - Button
Button("Of course") { // perform action }
-
2:41 - Toggle Favorite
Toggle(isOn: $favorite) { Label("Favorite", systemImage: "star") }
-
2:48 - TabView
TabView { DogsTab() .tabItem { Label("Dogs", systemImage: "pawprint") } CatsTab() .tabItem { Label("Cats", image: "cat") } BirdsTab() .tabItem { Label("Birds", systemImage: "bird") } }
-
3:37 - World App
@main struct WorldApp: App { var body: some Scene { WindowGroup("Hello, world") { ContentView() } } }
-
7:03 - World TabView
@main struct WorldApp: App { var body: some Scene { WindowGroup("Hello, world") { TabView { Modules() .tag(Tabs.menu) .tabItem { Label("Experience", systemImage: "globe.americas") } FunFactsTab() .tag(Tabs.library) .tabItem { Label("Library", systemImage: "book") } } } } }
-
8:42 - Stats Grid Section
VStack(alignment: .leading, spacing: 12) { Text("Stats") .font(.title) StatsGrid(stats: stats) .padding() .background(.regularMaterial, in: .rect(cornerRadius: 12)) }
-
9:23 - Fun Fact Button
Button(action: { // perform button action }) { VStack(alignment: .leading, spacing: 12) { Text(fact.title) .font(.title2) .lineLimit(2) Text(fact.details) .font(.body) .lineLimit(4) Text("Learn more") .font(.caption) .foregroundStyle(.secondary) } .frame(width: 180, alignment: .leading) } .buttonStyle(.funFact)
-
13:15 - FunFactButtonStyle
struct FunFactButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .padding() .background(.regularMaterial, in: .rect(cornerRadius: 12)) .hoverEffect() .scaleEffect(configuration.isPressed ? 0.95 : 1) } }
-
14:17 - Globe Volume
@main struct WorldApp: App { var body: some Scene { WindowGroup { Globe() } .windowStyle(.volumetric) .defaultSize(width: 600, height: 600, depth: 600) } }
-
14:36 - Model3D
import SwiftUI import RealityKit struct Globe: View { var body: some View { Model3D(named: "Earth") } }
-
15:40 - Globe with rotation and controls
struct Globe: View { @State var rotation = Angle.zero var body: some View { ZStack(alignment: .bottom) { Model3D(named: "Earth") .rotation3DEffect(rotation, axis: .y) .onTapGesture { withAnimation(.bouncy) { rotation.degrees += randomRotation() } } .padding3D(.front, 200) GlobeControls() .glassBackgroundEffect(in: .capsule) } } func randomRotation() -> Double { Double.random(in: 360...720) } }
-
17:30 - RealityView
RealityView { content in if let earth = try? await ModelEntity(named: "Earth") { earth.addImageBasedLighting() content.add(earth) } }
-
18:57 - RealityView Gesture
struct Earth: View { @State private var pinLocation: GlobeLocation? var body: some View { RealityView { content in if let earth = try? await ModelEntity(named: "Earth") { earth.addImageBasedLighting() content.add(earth) } } .gesture( SpatialTapGesture() .targetedToAnyEntity() .onEnded { value in withAnimation(.bouncy) { rotation.degrees += randomRotation() animatingRotation = true } completion: { animatingRotation = false } pinLocation = lookUpLocation(at: value) } ) } }
-
19:34 - RealityView Attachments
struct Earth: View { @State private var pinLocation: GlobeLocation? var body: some View { RealityView { content in if let earth = try? await ModelEntity(named: "Earth") { earth.addImageBasedLighting() content.add(earth) } } update: { content, attachments in if let pin = attachments.entity(for: "pin") { content.add(pin) placePin(pin) } } attachments: { if let pinLocation { GlobePin(pinLocation: pinLocation) .tag("pin") } } .gesture( SpatialTapGesture() .targetedToAnyEntity() .onEnded { value in withAnimation(.bouncy) { rotation.degrees += randomRotation() animatingRotation = true } completion: { animatingRotation = false } pinLocation = lookUpLocation(at: value) } ) } }
-
21:11 - ImmersiveSpace
@main struct WorldApp: App { var body: some Scene { // (other WindowGroup scenes) ImmersiveSpace(id: "solar-system") { SolarSystem() } } }
-
21:25 - Open ImmersiveSpace Action
@Environment(\.openImmersiveSpace) private var openImmersiveSpace Button("View Outer Space") { openImmersiveSpace(id: "solar-system") }
-
22:50 - ImmersionStyle
@main struct WorldApp: App { @State private var selectedStyle: ImmersionStyle = .full var body: some Scene { // (other WindowGroup scenes) ImmersiveSpace(id: "solar-system") { SolarSystem() } .immersionStyle(selection: $selectedStyle, in: .full) } }
-
23:17 - Starfield
struct Starfield: View { var body: some View { RealityView { content in let starfield = await loadStarfield() content.add(starfield) } } }
-
23:28 - SolarSystem
struct SolarSystem: View { var body: some View { Earth() Sun() Starfield() } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。