Highlight
visionOS App 开发沿用熟悉的 Xcode + SwiftUI 工作流,通过 Window、Volume、Immersive Space 三种场景类型和 RealityView 桥接 3D 内容,开发者可以在几小时内从零搭建出可交互的沉浸式体验。
核心内容
做 VR/AR 开发,过去最痛苦的是工具链。Unity 或 Unreal 虽然强大,但学习曲线陡峭,和 iOS 开发完全是两套体系。
visionOS 的做法不同:它把空间计算能力直接集成到开发者已经熟悉的工具里。Xcode 还是 Xcode,SwiftUI 还是 SwiftUI,只是多了几个新场景类型和一个 RealityView。
三种场景类型
创建 visionOS 项目时,Xcode 模板提供三种初始场景选择:(01:56)
Window:主要用于 2D 内容,平面尺寸可调,深度固定。和其他 App 并排显示在 Shared Space 中。适合传统 App 的迁移。
Volume:用于 3D 内容,三个维度的尺寸由 App 控制,用户不能调整。也运行在 Shared Space,但可以展示立体模型、数据可视化等内容。
Immersive Space:无边界的沉浸式场景,激活时 App 从 Shared Space 进入 Full Space,其他 App 被隐藏。可以请求 ARKit 功能如手部追踪。
Immersive Space 有三种沉浸风格:(03:50)
- Mixed:虚拟内容叠加在透视环境上,用户仍能看到真实世界
- Progressive:打开一个约 180 度的视口,用户可用 Digital Crown 调节视口大小
- Full:完全隐藏透视,用户被虚拟环境包围
Apple 建议 App 始终从 Window 开始,提供明确的按钮让用户主动进入沉浸模式。不要在用户不知情的情况下强制切换。(05:04)
RealityView:SwiftUI 和 3D 的桥梁
RealityView 是 visionOS 上连接 SwiftUI 和 RealityKit 的核心组件。它接收两个闭包:(07:28)
- make:异步初始化 RealityKit 内容,可以加载
.usda场景文件,也可以程序化创建 - update:在 SwiftUI 状态变化时调用,用于更新 3D 内容
update 闭包只在 SwiftUI 状态变化时触发,不是每帧调用的渲染循环。这个设计很重要,避免开发者误把它当游戏循环用。
从 Volume 到 Immersive Space
Session 演示了一个完整的开发流程:先用模板创建一个 Volume 项目,在 ContentView 里用 RealityView 加载一个 3D 球体模型,加上 Toggle 按钮和点击手势控制缩放。(06:24)
然后在 Reality Composer Pro 中创建一个新场景,导入 USDZ 云朵模型,调整位置和大小。回到 Xcode,在 App.swift 中添加 ImmersiveSpace 场景声明,在 ContentView 中添加按钮用 openImmersiveSpace 打开沉浸空间。(20:31)
Immersive Space 的坐标系以用户双脚位置为原点,X 轴向右,Y 轴向上,Z 轴负方向朝前。内容放置后位置固定,用户通过移动身体来探索空间。(16:04)
实体交互
让 3D 物体响应点击,需要两个条件:(25:48)
- 实体必须有
CollisionComponent,定义碰撞体积 - 实体必须有
InputTargetComponent,标记为可交互目标
这两个组件可以在 Reality Composer Pro 中添加,也可以用代码添加。然后给 RealityView 附加 .targetedToAnyEntity() 手势修饰符,就能精确识别用户点击了哪个实体。
Session 演示了点击云朵后让它飘动的效果,用 entity.move(to:relativeTo:duration:timingFunction:) 实现平滑动画。(28:56)
详细内容
Xcode 项目模板配置
创建 visionOS 项目时,Initial Scene 选择 Volume,Immersive Space 先选 None。项目生成后会包含:
MyFirstImmersiveApp.swift:App 入口,声明 WindowGroupContentView.swift:主视图,包含 RealityView 和 UI 控件RealityKitContentSwift Package:存放 3D 内容
// MyFirstImmersiveApp.swift
@main
struct MyFirstImmersiveApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}.windowStyle(.volumetric)
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
}
}
}
关键点:
.windowStyle(.volumetric)把 WindowGroup 变成 Volume,支持 3D 内容ImmersiveSpace需要唯一 ID,后续通过 ID 打开- 第一个 scene 是 App 启动时默认展示的
RealityView 的完整用法
RealityView { content in
// make 闭包:异步加载初始内容
if let scene = try? await Entity(named: "Scene", in: realityKitContentBundle) {
content.add(scene)
}
} update: { content in
// update 闭包:响应 SwiftUI 状态变化
if let scene = content.entities.first {
let uniformScale: Float = enlarge ? 1.4 : 1.0
scene.transform.scale = [uniformScale, uniformScale, uniformScale]
}
}
.gesture(TapGesture().targetedToAnyEntity().onEnded { _ in
enlarge.toggle()
})
关键点:
Entity(named:in:)从 RealityKit Content Package 加载场景,返回的是Entity类型content.add(scene)把加载的实体添加到 RealityView 的内容中content.entities可以访问已添加的所有实体transform.scale接受SIMD3<Float>,用数组字面量[x, y, z]简洁设置- 手势链式调用:先创建
TapGesture(),再用.targetedToAnyEntity()指定目标,最后用.onEnded处理事件
Glass Background Effect
visionOS 上的 UI 控件需要玻璃背景效果来保证可读性和交互性:
VStack {
Toggle("Enlarge RealityView Content", isOn: $enlarge)
.toggleStyle(.button)
}
.padding()
.glassBackgroundEffect()
关键点:
.glassBackgroundEffect()给视图添加平台标志性的毛玻璃背景- 这个效果会根据背后的 3D 内容自动调整透明度和模糊程度
- 所有可交互控件都应该包裹在这个效果中
打开 Immersive Space
struct ContentView: View {
@Environment(\.openImmersiveSpace) var openImmersiveSpace
var body: some View {
Button("Open") {
Task {
await openImmersiveSpace(id: "ImmersiveSpace")
}
}
}
}
关键点:
openImmersiveSpace是环境值,需要从@Environment获取- 它是异步的,用
await等待空间打开完成 - 传入的 ID 必须与
App中声明的ImmersiveSpaceID 匹配 - 必须在
Task中调用,因为 SwiftUI 的 body 不是异步上下文
实体点击动画
.gesture(TapGesture().targetedToAnyEntity().onEnded { value in
var transform = value.entity.transform
transform.translation += SIMD3(0.1, 0, -0.1)
value.entity.move(
to: transform,
relativeTo: nil,
duration: 3,
timingFunction: .easeInOut
)
})
关键点:
value.entity是被点击的具体实体,不是整个 RealityViewtransform.translation是SIMD3<Float>类型的位移向量move(to:relativeTo:duration:timingFunction:)是 RealityKit 的内置动画方法relativeTo: nil表示在世界坐标系中移动.easeInOut让动画有自然的加速减速效果
Xcode Preview 配置
预览 ImmersiveView 时,默认的预览边界会裁剪掉超出范围的内容。需要添加 .previewLayout(.sizeThatFits):
#Preview {
ImmersiveView()
.previewLayout(.sizeThatFits)
}
关键点:
#Preview宏替代了旧的PreviewProvider协议.sizeThatFits让预览根据内容自适应大小,不裁剪边界外的 3D 内容- 在 Simulator 中可以用同样的导航控制(look、pan、orbit、move)来观察场景
核心启发
1. 3D 产品展示器
做什么:做一个电商 App,让用户在 Volume 中 360 度查看商品模型,点击部件显示详情。
为什么值得做:RealityView + targetedToAnyEntity 让 3D 交互变得和 2D 一样简单,不再需要复杂的射线检测代码。
怎么开始:用 Reality Composer Pro 导入商品 USDZ 模型,给每个可交互部件添加 CollisionComponent 和 InputTargetComponent,在 SwiftUI 中用 .targetedToAnyEntity() 绑定点击手势,弹出详情面板。
2. 沉浸式数据可视化
做什么:把复杂数据集(如城市人口密度、股票走势)做成 3D 沉浸式图表,用户走进数据中探索。
为什么值得做:Immersive Space 让用户可以用身体移动来探索数据,比 2D 屏幕更直观。
怎么开始:用 RealityKit 程序化创建柱状图或散点图实体,放入 ImmersiveSpace 中,用 openImmersiveSpace 从 Window 中的按钮触发进入。
3. 虚拟空间导览 做什么:做一个博物馆或房产导览 App,从 Window 浏览列表,点击进入 Immersive Space 身临其境地参观。 为什么值得做:三种场景类型的组合让 App 可以平滑过渡从信息浏览到沉浸体验。 怎么开始:Window 中用 SwiftUI List 展示展品,选中后打开对应 ID 的 ImmersiveSpace,每个空间加载不同的 Reality Composer Pro 场景。
4. 手势驱动的 3D 创作工具
做什么:做一个简单的 3D 场景编辑器,用户点击实体可以移动、旋转、缩放。
为什么值得做:Entity targeting 和 RealityKit 动画 API 的组合让构建交互式 3D 编辑器变得可行。
怎么开始:给实体添加手势识别,在 .onEnded 中修改 transform,用 move(to:) 或 orientation 实现平滑动画过渡。
关联 Session
- Meet SwiftUI for spatial computing — SwiftUI 在空间计算平台上的新特性,理解 Window 和 Volume 的用法
- Go beyond the window with SwiftUI — 深入理解 Immersive Space 的三种沉浸风格和使用场景
- Build spatial experiences with RealityKit — RealityKit API 详解,包括 Entity、Component、Anchor 等核心概念
- Meet Reality Composer Pro — Reality Composer Pro 工具入门,学习如何准备和预览 3D 内容
评论
GitHub Issues · utterances