Take SwiftUI to the next dimension
Swift & UI 进阶 20m

SwiftUI 进入第三维:Volume、Model3D 与 3D 手势

Take SwiftUI to the next dimension

2023年6月5日

在 Apple 官方观看视频

一句话判断

从 Model3D 加载 USDZ 到 RealityView 的 attachments API,这场 Session 是 SwiftUI 3D 内容开发的实操手册。

这场 Session 讲了什么

Mark 从 SwiftUI 团队出发,以 World 示例 App 为载体,讲解了在 Volume 容器中使用 SwiftUI 展示 3D 内容的完整流程:

Volume 容器。使用 .windowStyle(.volumetric) 创建固定尺寸的 3D 容器。Volume 保持真实尺寸不变——不管距离多远,1 米宽的 Volume 永远是 1 米宽。支持从任何角度查看。

Model3D 视图。加载 USDZ 文件的 SwiftUI 视图,类似 AsyncImage 的设计。提供 loading/success/failure 三个阶段,用 .resizable().scaledToFit() 控制尺寸。默认背面对齐,可以用 .frame(depth:)depthAlignment 改为正面对齐。

3D 布局和动画。用 TimelineView 驱动基于时间的动画,配合 Rotation3DEffect 实现 3D 旋转。containerRelativeFrame 让视图尺寸相对于容器而非设备屏幕,配合 horizontalSizeClass 做自适应布局。

RealityView 和 Attachments。RealityView 是 SwiftUI 中使用 RealityKit 的入口。Attachments API 让你把 SwiftUI 视图(文字标签、编辑控件)绑定到 RealityKit Entity 上。这些视图是活的——可以响应状态变化、运行动画、处理手势。

3D 手势。在 Volume 中支持拖拽、旋转、缩放等空间手势。

值得深挖的点

Model3D 的深度对齐。3D 内容默认背面与容器对齐,这在从侧面看时会显得不自然。设置 depthAlignment: .front 让内容正面对齐,从任何角度观察都更协调。

Attachments 的”活视图”特性。和 Canvas API 的静态快照不同,Attachments 中的 SwiftUI 视图是真实存在的视图——它们会响应 @State 变化、运行动画、处理用户交互。这让 3D 场景中的 2D 标签变得非常灵活。

containerRelativeFrame 的跨平台价值。这个 API 不只适用于空间计算——在 iOS/iPadOS 上也能用。让视图尺寸基于最近的容器(ScrollView 列、NavigationSplitView 列、窗口)而非整个屏幕,布局更精确。

代码片段

// Model3D 加载 3D 模型
Model3D(named: "Moon") { phase in
    switch phase {
    case .success(let model):
        model.resizable().scaledToFit()
    case .failure(let error):
        Text(error.localizedDescription)
    default:
        ProgressView()  // 加载中
    }
}
.frame(depth: 100, depthAlignment: .front)  // 正面对齐
// 3D 旋转动画
TimelineView(.animation) { context in
    CelestialObjectView(name: "Earth")
        .rotation3DEffect(
            .degrees(context.date.timeIntervalSinceReferenceDate * 10),
            axis: (x: 0, y: 1, z: 0)
        )
}
// RealityView + Attachments:在 3D 场景中放置 SwiftUI 标签
RealityView { content in
    let earth = try await Entity(named: "Earth")
    content.add(earth)
} attachments: {
    // 为每个地点创建标签
    ForEach(favoritePlaces) { place in
        Text(place.name)
            .glassBackgroundEffect()
            .tag(place.id)  // 用 tag 标识
    }
}
// attachments 会自动匹配到 RealityKit Entity

最佳实践

  • 简单的 3D 展示用 Model3D,需要自定义交互和渲染用 RealityView。
  • 注意 3D 内容的深度对齐,默认的背面模式可能不是你想要的。
  • 用 containerRelativeFrame 做基于容器的自适应布局,配合 horizontalSizeClass 区分设备。
  • Attachments 中的视图可以放任何 SwiftUI 组件,包括按钮和手势。
  • 给 3D 内容添加玻璃背景效果的标签,确保在任何背景下都可读。

还有什么值得关注

  • “Build spatial experiences with RealityKit” 是 RealityView 的姊妹 Session
  • “Enhance your spatial computing app with RealityKit” 讲了更多 RealityKit 高级功能
  • “Elevate your windowed app for spatial computing” 讲了窗口 App 的优化
  • World 示例 App 是学习 3D SwiftUI 的最佳参考项目
WWDC 2023