View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

返回 WWDC25

  • 简介
  • 概要
  • 转写文稿
  • 代码
  • 了解 SwiftUI 空间布局

    探索使用 SwiftUI 开发空间体验的新工具。了解 visionOS 上 3D SwiftUI 视图的基本知识,借助深度对齐功能来自定现有布局,并使用修饰符在空间中旋转和放置视图。探索如何使用空间容器在同一 3D 空间中对齐视图,打造更沉浸、更引人入胜的 App。

    章节

    • 0:00 - 简介
    • 2:47 - 3D 视图
    • 7:18 - 深度对齐
    • 11:41 - 旋转布局
    • 16:28 - 空间容器
    • 19:22 - 后续步骤

    资源

    • Canyon Crosser: Building a volumetric hike-planning app
    • Human Interface Guidelines: Designing for visionOS
      • 高清视频
      • 标清视频

    相关视频

    WWDC25

    • 搭配使用更出色:SwiftUI 和 RealityKit

    WWDC22

    • 使用 SwiftUI 构建自定布局

    WWDC19

    • 利用 SwiftUI 构建自定视图
  • 搜索此视频…

    大家好 欢迎观看 “SwiftUI 空间布局简介” 我是 Trevor SwiftUI 团队的工程师 在本次讲座中 我们将一起探索如何 通过 SwiftUI 构建赏心悦目的空间体验

    我最近在使用 SwiftUI 的全新空间布局功能 扩展我非常喜欢的一款 App 名叫“BOT-anist”

    这款 App 可以让你 用不同的构建块、颜色和材质 自定有趣的机器人 然后用这些全新打造的机器人来 打理你的虚拟花园 我非常喜欢构建这些小机器人 最近也在设计一些新视图 用来整理我创造的作品

    现在 你不仅可以自定机器人 还能保存并收藏你制作的机器人合集

    我迫不及待想展示一些用于浏览 机器人作品的全新 3D 场景 而这些体验都是用 SwiftUI 构建的

    如果你之前在 visionOS 上 构建过 3D 体验 可能已经用过 RealityKit

    RealityKit 这款框架 非常适合用来构建 3D App 尤其适合构建具备复杂行为的 App 如物理模拟

    但如果你熟悉的是 SwiftUI 你可能更想继续使用声明式语法 来构建界面 而且你也未必在整个 App 中都需要 RealityKit 那么强大的功能 现在 在 visionOS 26 中 你可以用 SwiftUI 现有的 2D 布局工具 和理念来构建 3D 应用程序

    使用 SwiftUI 布局时 你能借助内置支持 轻松实现动画效果、尺寸调整 和状态管理 举个例子 如果我从转盘中 移除一个机器人 SwiftUI 会自动以动画效果 调整其余机器人的位置和大小 以便适应空间的变化

    而调整空间容器大小时 转盘和其中的每个机器人 也会自动随之缩放 接下来 让我们深入了解 构建这种自动排列布局的新工具

    不过在此之前 我想提醒一下 这些 SwiftUI 的 3D 扩展布局系统 是基于现有的 2D 布局概念构建的

    如果你还不熟悉 SwiftUI 布局 建议先观看往年的两个讲座: “利用 SwiftUI 构建自定视图” 和“使用 SwiftUI 构建自定布局” 本视频将介绍 visionOS 上 3D SwiftUI 视图的基础知识 如何通过深度对齐来自定现有布局 如何使用 rotation3DLayout 这个新修饰符在布局系统中旋转视图 最后 你将了解 SpatialContainer 和 spatialOverlay 如何助你在同一 3D 空间中 对齐多个视图

    首先 我们来聊聊视图与布局系统

    对于 App 中的每个视图 SwiftUI 都会计算宽度、高度 以及 X 和 Y 位置

    有些视图 比如不可缩放的图像 拥有固定边框 大小与素材本身相符

    而像 Color 这样的视图 则具有弹性边框 会占据父视图提供的所有空间

    布局会将子视图组合成最终的边框 这个以黄色显示的 VStack 边框 它的大小取决于可用空间 和其中包含的子视图 在这个例子中 它的高度 就是内部两个图像视图高度的总和 visionOS 也是同样的机制 只不过视图现在是 3D 的 布局系统的行为没有变 只是从二维扩展到了三维

    这意味着 SwiftUI 不仅会为每个视图计算宽度和高度 还会额外计算深度和 Z 位置 我常在 iOS 上使用 border 修饰符 来直观地呈现 2D 边框

    在这里 我自定义了一个 debugBorder3D 修饰符 用来在 visionOS 上 直观呈现 3D 边框 关于这个修饰符是如何实现的 我会在本视频最后讲解 到时还会用到稍后介绍的两个 API

    通过 debugBorder3D 你可以看到 Model3D 和 Image 类似 但呈现的是三维视图 而非二维 它具有固定的宽度、高度和深度 虽然所有视图都是 3D 的 但有些视图的深度为零

    很多用于构建平面体验的视图 比如 Image、Color 和 Text 都是零深度 它们的表现就和 在 iOS 上完全一样

    也有一些视图具有弹性深度 就像 Color 默认会占据 所有可用的宽度和高度一样 在 visionOS 中 像 RealityView 这样的视图 会默认占满所有可用深度

    GeometryReader3D 同样具备这种弹性尺寸特性

    加上 resizable 修饰符的 Model3D 也是如此 我们的机器人也因此像被拉伸成 太妃糖一样 填满了整个窗口宽度 在这个长宽比下 机器人的脸看起来有点拉长 我希望在适应可用空间的同时 恢复机器人原本的比例

    我可以在 resizable() 的基础上 加上新的 scaledToFit3D 修饰符 让机器人在缩放时 保持模型的原始比例 同时适应可用的宽度、高度 以及如今增添的深度

    那么 这个“可用深度” 是从哪来的呢? 就像宽度和高度一样 窗口内容 会收到一个根深度建议 不过与可调整的宽高不同 窗口的深度建议是固定的 超出这个深度范围的部分 可能会被系统裁剪

    空间容器同样会向内容提供 建议的宽度、高度和深度 但在空间容器中 深度是可调的 请参考《人机界面指南》中的 “为 visionOS 设计” 详细了解窗口和空间容器的适用场景

    有些视图允许修改 对所含视图的深度建议 就像 VStack 会组合子视图的高度一样 ZStack 也会组合子视图的深度 比如这里 这个 ZStack 的深度正好能容纳 两个机器人前后叠放的空间

    与 VStack 类似 ZStack 也会根据可用空间、 子视图数量和类型等因素 向每个子视图 提出不同的 深度建议 在这个例子中 RealityView 会在 ZStack 中把机器人往前推 填满整个场景中的可用深度

    在 visionOS 中 现有的 Layout 和 Stack 类型都是 3D 的 并会为深度应用合理的默认行为 比如这个 HStack 会继承 来自父视图的深度建议 并自行确定深度 以紧贴内部的两个模型

    默认情况下 HStack 还会对齐两个机器人的背面

    我们将这种行为称为“深度对齐” 深度对齐是一种全新的工具 可用于自定现有的 SwiftUI 布局类型 以更好地适应 3D 视图和深度 如果你用过垂直或水平对齐 那你会觉得这种方式非常熟悉 我想构建一个新的视体窗口 用来展示我最喜欢的机器人 并附上它们的名称和简介 首先 我们来更新机器人 Model3D 的代码 让它更具复用性

    先构建一个能够缩放 以适应可用空间的 Model3D

    然后重构为使用新的 Model3DAsset 类型 方便预载入机器人的模型 接着 我将这些内容封装成 一个新的 ResizableRobotView 这样就能在整个 App 中重复使用了 我也暂时移除了 debugBorder3D

    现在 用 VStack 创建 RobotProfile 其中包含 ResizableRobotView 和一个显示机器人信息的 RobotNameCard

    不过目前有个问题

    这张卡片位于 VStack 的背面 看起来被机器人挡住了 不太容易看清

    就像你可以设置 HStack 的对齐方式 选择将内容对齐到中间、 顶部或底部边缘一样 在 visionOS 中 你也可以 设置视图在深度方向的对齐方式

    Stack 和 Layout 类型 默认的深度对齐方式为 .back 而在 visionOS 26 中 你可以 为任何 Layout 类型自定 深度对齐方式

    我将 RobotProfile 更新为使用 VStackLayout

    这样就可以应用 depthAlignment 修饰符 在这里 我将对齐方式设置为 .front

    当然 你也可以选择 .center 或 .back 对齐

    但要让这张机器人信息卡清晰可见 选择 .front 应该没错

    现在 我肯定不会忘记 Zapper Ironheart 和它那百科全书般的冷知识储备了

    标准的 .front、.back 和 .center 深度对齐方式很适合常规场景 但如果你想实现更复杂的行为 该怎么办呢?

    我正在创建一个空间容器 用来展示我最喜欢的三个机器人 HStack 中包含三个 RobotProfile 其中我最喜欢的是 Greg-gear Mendel 我希望它在视图中 更突出一些

    于是我想到用一种 “深度领奖台”的方式 让我更喜欢的机器人离我更近 所以机器人 1 离我最近 然后是 2 最后是 3

    从上往下看 效果像这样: 第一个机器人的背面在深度上 与第二个机器人的中心 和第三个机器人的正面对齐 我需要借助自定深度对齐 来实现这种效果

    首先 定义一个结构体 遵从 DepthAlignmentID 协议

    并实现所需的 默认对齐值

    在这里 将 DepthPodiumAlignment 的默认值设为 .front 对齐

    接着 为 DepthAlignment 定义一个静态常量 使用这个新的 DepthAlignmentID 类型

    现在 我就可以使用 depthPodium 为包含各个机器人视图的 HStack 提供深度对齐参考

    这样一来 所有机器人都会默认 按正面对齐 因为我们为这个对齐方式设置的 默认值是 .front

    接下来 我将为尾部的机器人 自定 depthPodium 对齐方式 使它的深度中心 与这个对齐参考线对齐

    再将中间机器人的背面对齐到 depthPodium

    而前排机器人则继续使用 默认的正面对齐

    我们来看看模拟器中的效果

    通过错落的深度排布 Greg-gear Mendel 显然就是我最爱的机器人

    当你想在现有布局中微调 视图的深度位置时 深度对齐非常实用 但如果你想构建更注重深度的体验 Rotation Layout 会是一个强大的 工具 适合更高级的 3D 用例 你可能已经熟悉现有的 rotation3DEffect 修饰符 它可以让视图围绕指定轴旋转 并呈现出视觉效果

    这个修饰符非常适合 用来实现基础的旋转效果

    但如果我们将模型和描述卡片 放在一个 HStack 中 再将火箭模型沿 Z 轴旋转 90 度 它就会碰到卡片 甚至超出空间容器的边界

    如果在旋转前后加上调试线框 会更容易看清楚 究竟是哪里出现了问题 红色实线框显示的是 应用旋转后的效果 而蓝色虚线框代表布局系统 认定的火箭位置 HStack 会根据蓝框来调整大小 并排布内容 两者并不一致 这是因为视觉效果不会影响布局 也就是说 虽然使用了 rotation3DEffect HStack 并不知道火箭已被旋转

    这一点同样适用于 scaleEffect 和

    offset 等所有视觉效果

    在这些情况下 即使添加了 这种修饰符 布局系统也不会 调整视图的尺寸或位置 如果你只想为某个视图添加动画效果 而不影响周围布局 这种机制恰好能满足你的需求

    但如果你希望布局随之改变呢? 我们该如何修正这个被旋转的火箭?

    好消息是 在 visionOS 26 中 我们新增了 rotation3DLayout 修饰符 它会将旋转后的视图 纳入布局系统的计算 将这个修饰符应用到火箭模型后 HStack 就能自动调整尺寸和位置 为火箭和描述卡片留出充足空间

    rotation3DLayout 支持任意角度和轴向的旋转 所以我可以将火箭旋转 45 度 看起来就像真的要发射升空一样

    我在 rotation3DLayout 修饰符 前后加上调试线框 红色表示旋转后的视图边框 蓝色则是布局系统中识别的 修改后视图边框 可以看到 蓝色边界框是与父视图 轴向对齐的 并紧贴红色的旋转框

    接下来 我们来看看 如何用 rotation3DLayout 构建开头展示的机器人转盘

    首先 我会借用“使用 SwiftUI 构建 自定布局”中介绍的 RadialLayout

    这个自定 Layout 类型 会将视图沿圆周排列 圆的大小由可用宽高决定

    MyRadialLayout 最初是为 iOS 上的 2D 布局设计的

    但在 visionOS 上同样适用

    即使是摆放 3D 机器人模型 也和摆放 2D 宠物图像一样

    我们可以用 ForEach 将每个机器人的可缩放 Model3D 添加到这个布局中

    看起来不错 但现在还是 垂直排列的体验 我希望机器人在空间容器中 呈水平排列

    我为整个圆形布局应用一个 rotation3DLayout 让视图沿 X 轴旋转 90 度 原本的转盘高度 现在定义的是布局系统中 旋转视图的深度 转盘方向对了 但机器人都躺下了 像是在偷懒

    我们可以逆时针旋转每个机器人 让它们站起来 为此 在 ForEach 中 再加一个 rotation3DEffect 将沿 X 轴旋转的角度设为 -90 度 这些昏昏欲睡的小机器人 现在就立正站好了 还剩最后一处需要调整 现在 在空间容器中 转盘处于垂直居中的高度 但我希望它贴近底部 与空间容器的底板对齐

    在整个转盘中加上 debugBorder3D 后 这点会更清晰

    我可以采用与 2D 布局 相同的处理方式 使用 VStack 在这段代码上方添加 一个 Spacer 把转盘“压”到底部 现在 机器人们整齐地站在 空间容器的底部 看起来很不错 接下来介绍 3D 布局工具箱中 的另一对实用工具 SpatialContainer 和 spatialOverlay 我还想为这个机器人转盘 加上另一个功能 轻点机器人时 应该会选中它 并显示一个控制菜单 相应的模型底部还会显示一个圆环 表明选中了这个机器人 这个圆环本身也是一个 Model3D 我们希望这个圆环与机器人 共享同一 3D 空间 而不是沿某个轴堆叠 因此 我们需要一种新工具 来让多个模型共享同一个 3D 空间

    新增的 SpatialContainer API 就是这样的工具 它可以像套娃一样 把多个视图 放进同一个 3D 空间中

    你可以为所有视图 统一设置三维对齐方式 比如这里 我们使用 .bottomFront 对齐全部子视图

    还有这里 采用 .topTrailingBack 对齐方式

    spatialOverlay 是类似的工具 可将一个视图叠加到 同一个 3D 空间中的另一个视图上

    与 SpatialContainer 类似 它同样支持 3D 对齐

    在这里 我只需要对齐 机器人和选中圆环这两个视图 我更在意机器人的几何形状 对于圆环 只要它可以根据 机器人尺寸自动缩放 那就行了 所以 我们用 spatialOverlay 来实现选中机器人的视觉效果

    我在机器人模型上添加一个 spatialOverlay 修饰符 如果当前机器人处于选中状态 就显示可缩放圆环视图 我们使用 .bottom 对齐方式 让圆环的底部与机器人底部对齐

    现在 整个转盘看起来非常棒 而且借助现有的 SwiftUI 组合式 API 还能轻松继续优化

    最后 让我们通过 debugBorder3D 修饰符的实现流程 来复习一下所学的内容

    这是刚刚介绍的修饰符 我已经将它应用到了 Model3D 上

    我将 debugBorder3D 定义为 View 的扩展方法 我为修改后的内容添加 spatialOverlay 这样我们就能在目标视图 所处的同一 3D 空间中 对边框进行渲染

    我在内部放置一个 ZStack 里面包含 一个 2D 边框 一个 Spacer 以及另一个 2D 边框

    接着 我为整个 ZStack 应用 rotation3DLayout 将两个边框分别放在视图的左右两侧

    最后 我再将这个内层 ZStack 放入 另一个 ZStack 为视图前后两面添加 2D 边框 这样一来 所有边都加上了边框

    我很喜欢能把现有的 2D SwiftUI 修饰符与 全新的 3D API 组合起来 打造出全新的体验

    对于很多常用的 2D 布局工具和修饰符 我们都推出了相应的 3D 工具和修饰符 欢迎查阅文档 了解更多相关 API

    SwiftUI 是构建 3D App 的强大工具 但在很多用例中 你可能还是需要用到 RealityKit 要打造一款 App 通常需要将两者结合使用

    现在你的 SwiftUI 内容 已经升级为 3D 可能需要结合 RealityKit 代码 来进一步优化 我的朋友 Maks 和 Amanda 就结合使用这两个框架 为“BOT-anist”构建了 出色的附加功能 欢迎观看“搭配使用更出色: SwiftUI 和 RealityKit”了解更多 我已经迫不及待想看到 你构建的 3D App 了!

    • 3:02 - Robot Image Frame

      // Some views have fixed frames
      
      Image("RobotHead")
        .border(.red)
    • 3:05 - Color Frame

      // Some views have flexible frames
      
      Color.blue
        .border(.red)
    • 3:15 - Layout Composed Frame

      // Layouts compose the frames of their children
      
      VStack {
        Image("RobotHead")
          .border(.red)
        Image("RobotHead")
          .border(.red)
      }
      .border(.yellow)
    • 4:00 - Model3D Frame

      // Some views have fixed depth
      
      Model3D(named: "Robot")
        .debugBorder3D(.red)
    • 4:25 - Zero Depth Views

      // Many views have 0 depth
      
      HStack {
        Image("RobotHead")
          .debugBorder3D(.red)
        Text("Hello! I'm a piece of text. I have 0 depth.")
          .debugBorder3D(.red)
        Color.blue
          .debugBorder3D(.red)
          .frame(width: 200, height: 200)
      }
    • 4:41 - RealityView Depth

      // RealityView takes up all available space including depth
      
      RealityView { content in
        // Setup RealityView content
      }
      .debugBorder3D(.red)
    • 4:56 - GeometryReader3D Depth

      // GeometryReader3D uses all available depth
      
      GeometryReader3D { proxy in
        // GeometryReader3D content
      }
      .debugBorder3D(.red)
    • 5:01 - Model3D scaledToFit3D

      // Scaling a Model3D to fit available space
      
      Model3D(url: robotURL) {aresolved in
        resolved.resizable()
      }aplaceholder: {
        ProgressView()
      }
      .scaledToFit3D()
      .debugBorder3D(.red)
    • 6:15 - ZStack depth

      // ZStack composes subview depths
      
      ZStack {
        Model3D(named: "LargeRobot")
          .debugBorder3D(.red)
        Model3D(named: "BabyBot")
          .debugBorder3D(.red)
      }
      .debugBorder3D(.yellow)
    • 6:33 - ZStack with RealityView

      // ZStack composes subview depths
      
      ZStack {
        RealityView { ... }
          .debugBorder3D(.red)
        Model3D(named: "BabyBot")
          .debugBorder3D(.red)
      }
      .debugBorder3D(.yellow)
    • 6:57 - Layouts are 3D

      // HStack also composes subview depths
      
      HStack {
        Model3D(named: "LargeRobot")
          .debugBorder3D(.red)
        Model3D(named: "BabyBot")
          .debugBorder3D(.red)
      }
      .debugBorder3D(.yellow)
    • 7:50 - ResizableRobotView

      struct ResizableRobotView: View {
        let asset: Model3DAsset
      
        var body: some View {
          Model3D(asset: asset) { resolved in
            resolved
              .resizable()
          }
          .scaledToFit3D()
        }
      }
    • 8:11 - Robot Profile 1

      //`Layout` types back align views by default
      
      struct RobotProfile: View {
        let robot: Robot
      
        var body: some View {
          VStack {
            ResizableRobotView(asset: robot.model3DAsset)
            RobotNameCard(robot: robot)
          }
          .frame(width: 300)
        }
      }
    • 8:38 - Customizing Vertical Alignment

      // Customizing vertical alignment
      
      HStack(alignment: .bottom) {
        Image("RobotHead")
          .border(.red)
        Color.blue
          .frame(width: 100, height: 100)
          .border(.red)
      }
      .border(.yellow)
    • 8:52 - Customizing Depth Alignment

      // Customizing depth alignments
      
      struct RobotProfile: View {
        let robot: Robot
      
        var body: some View {
          VStackLayout().depthAlignment(.front) {
            ResizableRobotView(asset: robot.model3DAsset)
            RobotNameCard(robot: robot)
          }
          .frame(width: 300)
        }
      }
    • 9:45 - Robot Favorite Row

      struct FavoriteRobotsRow: View {
        let robots: [Robot]
      
        var body: some View {
          HStack {
            RobotProfile(robot: robots[2])
            RobotProfile(robot: robots[0])
            RobotProfile(robot: robots[1])
          }
        }
      }
    • 10:27 - Custom Depth Alignment ID

      // Defining a custom depth alignment guide
      
      struct DepthPodiumAlignment: DepthAlignmentID {
        static func defaultValue(in context: ViewDimensions3D) -> CGFloat {
          context[.front]
        }
      }
      
      extension DepthAlignment {
        static let depthPodium = DepthAlignment(DepthPodiumAlignment.self)
      }
    • 10:51 - Customizing Depth Alignment Guides

      // Views can customize their alignment guides
      
      struct FavoritesRow: View {
        let robots: [Robot]
      
        var body: some View {
          HStackLayout().depthAlignment(.depthPodium) {
              RobotProfile(robot: robots[2])
              RobotProfile(robot: robots[0])
                .alignmentGuide(.depthPodium) {
                  $0[DepthAlignment.back]
                }
              RobotProfile(robot: robots[1])
            		.alignmentGuide(.depthPodium) {
                  $0[DepthAlignment.center]
                }
          }
        }
      }
    • 12:00 - Rotation3DEffect

      // Rotate views using visual effects
      
      Model3D(named: "ToyRocket")
        .rotation3DEffect(.degrees(45), axis: .z)
    • 12:10 - Rotation3DLayout

      // Rotate using any axis or angle
      
      HStackLayout().depthAlignment(.front) {
        RocketDetailsCard()
        Model3D(named: "ToyRocket")
        	.rotation3DLayout(.degrees(isRotated ? 45 : 0), axis: .z)
      }
    • 14:42 - Pet Radial Layout

      // Custom radial Layout
      
      struct PetRadialLayout: View {
        let pets: [Pet]
      
        var body: some View {
          MyRadialLayout {
            ForEach(pets) { pet in
              PetImage(pet: pet)
            }
          }
        }
      }
    • 14:56 - Rotated Robot Carousel

      struct RobotCarousel: View {
        let robots: [Robot]
      
        var body: some View {
      		VStack {
            Spacer()
            MyRadialLayout {
              ForEach(robots) { robot in
                ResizableRobotView(asset: robot.model3DAsset)
                	.rotation3DLayout(.degrees(-90), axis: .x)
              }
            }
            .rotation3DLayout(.degrees(90), axis: .x)
        }
      }
    • 17:00 - Spatial Container

      // Aligning views in 3D space
      
      SpatialContainer(alignment: .topTrailingBack) {
        LargeBox()
        MediumBox()
        SmallBox()
      }
    • 17:35 - Spatial Overlay

      // Aligning overlayed content
      
      LargeBox()
        .spatialOverlay(alignment: .bottomLeadingFront) {
          SmallBox()
        }
    • 17:47 - Selection Ring Spatial Overlay

      struct RobotCarouselItem: View {
        let robot: Robot
        let isSelected: Bool
      
        var body: some View {
          ResizableRobotView(asset: robot.model3DAsset)
      			.spatialOverlay(alignment; .bottom) {
              if isSelected {
                ResizableSelectionRingModel()
              }
        }
      }
    • 18:32 - DebugBorder3D

      extension View {
        func debugBorder3D(_ color: Color) -> some View {
          spatialOverlay {
      			ZStack {
      				Color.clear.border(color, width: 4)
              ZStack {
                Color.clear.border(color, width: 4)
                Spacer()
                Color.clear.border(color, width: 4)
              }
              .rotation3DLayout(.degrees(90), axis: .y)
      				Color.clear.border(color, width: 4)
            }
          }
        }
    • 0:00 - 简介
    • SwiftUI 在 visionOS 26 中新增了 3D 布局功能,让你能使用 SwiftUI 的声明式语法来构建 3D App。这些功能基于现有的 2D 布局概念构建,并为动画、大小调整和状态管理提供内置支持。BOT-anist 示例 App 演示了用户如何在虚拟花园中自定机器人并为它们编制目录。 如果你刚开始接触 SwiftUI 布局,请先观看“利用 SwiftUI 构建自定视图”和“使用 SwiftUI 构建自定布局”,然后再深入探究这个视频的内容。

    • 2:47 - 3D 视图
    • 在 SwiftUI 中,布局系统会计算 App 中每个视图的宽度、高度以及 X 和 Y 位置。某些视图的边框是固定的,也有一些边框具有弹性,可占据父视图提供的空间。 在 visionOS 中,这个概念延伸到了三维空间。现在,除了宽度和高度之外,每个视图还具有深度和 Z 位置。布局系统的行为类似于 2D,但会根据 3D 空间适应调整。视图的深度可以为固定、弹性或零深度。 带有“resizable”修饰符的 GeometryReader3D 和 Model3D 可以占据所有可用的深度。新的“scaledToFit3D”修饰符可在调整大小时保持宽高比。窗口的根深度建议是固定的,而视体的深度则可调整。超出这个深度的内容可能会被系统裁剪。 ZStack 可以像 VStack 组合高度一样组合深度,而现有的布局类型 (如 HStack 和 VStack) 会在 visionOS 中自动转换为 3D,并对深度应用合理的默认行为,例如沿着元素的背面排列元素。

    • 7:18 - 深度对齐
    • 深度对齐是 visionOS 26 中的一项新功能,可用于在 3D 空间中自定视图的定位,类似于 2D 中的垂直和水平对齐。这种方法在创建视体窗口或显示 3D 模型时特别有用。 这个示例演示了如何使用深度对齐来让机器人简介视图更加清晰易读。将“.front”深度对齐修饰符应用于机器人信息卡后,信息卡会移动到最前面,变得更容易看到。 对于更复杂的场景,你可以创建自定深度对齐。这个示例展示了如何自定“DepthPodiumAlignment”,在深度上错落排布三个机器人简介视图,让最喜欢的机器人最靠近观看者,使它更加显眼。

    • 11:41 - 旋转布局
    • visionOS 26 引入了新的“rotation3DLayout”修饰符,以解决现有“rotation3DEffect”修饰符的限制。“rotation3DEffect”修饰符仅应用视觉旋转而不影响布局系统,这会导致在 HStacks 等容器中旋转视图时出现问题。当你想在不影响其他视图边框的情况下为视图添加动画效果时,“rotation3DEffect”仍然很有用。 “rotation3DLayout”修饰符会在布局系统中为旋转后的视图修改边框,进行适当的大小和位置调整,适合更复杂的 3D 布局。例如,你可以使用这个修饰符,沿 X 轴将自定 RadialLayout 旋转 90 度,创建水平方向的机器人转盘。然后,转盘中的每个机器人都会反向旋转,这样就会站起来。你还可以应用其他调整,例如使用 VStack 和 Spacer,将转盘“压”到视体底部,打造出精美且具有视觉吸引力的 3D 用户界面。

    • 16:28 - 空间容器
    • “SpatialContainer”和“SpatialOverlay”是 SwiftUI 中用于 3D 布局的新工具。SpatialContainer 支持使用不同对齐方案在 3D 空间中放入多个视图,而 SpatialOverlay 则可以将一个视图叠加到另一个视图上。 这个示例使用 SpatialOverlay 为转盘中的机器人创建了一个选中圆环,紧贴在机器人底部。“debugBorder3D”修饰符的示例作为 View 的扩展方法进行演示,使用“SpatialOverlay”、ZStacks 和“rotation3DLayout”将 3D 边框添加到任何 Model3D 以进行调试。

    • 19:22 - 后续步骤
    • SwiftUI 现在支持使用你熟悉的 2D 修饰符和新的 3D API 进行 3D App 开发。你可以将 SwiftUI 与 RealityKit 结合使用,以增强功能。请观看“搭配使用更出色:SwiftUI 和 RealityKit”,通过示例了解这种整合。

Developer Footer

  • 视频
  • WWDC25
  • 了解 SwiftUI 空间布局
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则