WWDC Quick Look 💓 By SwiftGGTeam
Get started with building apps for spatial computing

Get started with building apps for spatial computing

观看原视频

Highlight

visionOS 的空间计算平台基于 SwiftUI、RealityKit 和 ARKit 三个熟悉的框架,通过 Window、Volume 和 Full Space 三种容器,让开发者可以用同一套代码构建从 2D 窗口到完全沉浸式体验的应用。

核心内容

三种空间容器

visionOS 的核心设计围绕三种容器展开(01:27):

Window(窗口)

Window 是 SwiftUI 的 Scene,可以调整大小、重新布局,和 macOS 窗口的行为类似。但它支持混合 2D 和 3D 内容,你可以在一个窗口里同时放按钮和一个 3D 模型。

Volume(体积)

Volume 是在 Shared Space 中展示 3D 内容的固定边界容器(02:07)。它适合展示棋盘、地球仪这类可以从不同角度查看的 3D 内容。Volume 也是 SwiftUI Scene,用 RealityKit 渲染 3D。

Full Space(全空间)

Full Space 让你的应用独占整个视野,其他应用被隐藏(02:47)。在 Full Space 中可以使用 ARKit 的 API,比如骨骼手部追踪(Skeletal Hand Tracking)。Full Space 有三种沉浸风格:

  • Mixed:在真实环境(passthrough)上叠加虚拟内容
  • Full:完全隐藏真实环境,只显示虚拟内容
  • Progressive:初始显示真实环境,用户可以通过数码表冠调节沉浸程度

自然交互方式

visionOS 的交互基于眼睛和双手(04:30):

  • 看着按钮,手指轻触(tap)即可选中
  • 可以直接伸手触摸 3D 空间中的按钮
  • 系统支持 tap、long press、drag、rotate、zoom 等手势
  • SwiftUI 的手势 API 和 RealityKit Entity 无缝集成

ARKit 的骨骼手部追踪让更复杂的手势成为可能,比如把用户的手变成虚拟球杆来打保龄球(05:36)。

隐私优先的架构

visionOS 的隐私设计很严格(07:47)。应用不能直接访问传感器数据,系统会把原始数据转换成事件和视觉提示再交给应用。比如:

  • 系统知道用户眼睛和手的 3D 位置,但只传递 touch 事件给应用
  • 当用户注视某个视图时,系统会渲染 hover 效果,但不会告诉应用用户在看哪里
  • 需要访问场景理解(检测墙壁、家具)或骨骼手部追踪时,系统会先征求用户许可

详细内容

开发工具链

Xcode 为 visionOS 提供了完整的开发工具(09:05):

Xcode Preview

Preview Canvas 支持 3D 预览,可以直接在代码编辑时看到 RealityKit 场景的渲染结果,包括动画效果(09:34)。Object Mode 可以快速预览 3D 布局,检查内容是否在视图边界内。

Simulator

Simulator 支持用键盘、鼠标或游戏手柄在场景中移动和观察。自带三种模拟场景,每种都有白天和夜晚光照(11:03)。

RealityKit Trace

Instruments 15 新增了 RealityKit Trace 模板,可以分析 GPU、CPU 和系统功耗影响,追踪帧瓶颈,查看提交的三角形总数和模拟的 RealityKit Entity 数量(12:24)。

Reality Composer Pro

新的 3D 内容编辑工具,支持:

  • 粒子系统(云、雨、火花等效果)
  • 空间音频预览
  • 基于物理的材质(PBR)
  • MaterialX 自定义材质节点图(14:32

创建 Window

Window 通过 WindowGroup 创建,可以混合 2D 和 3D 内容:

import SwiftUI
import RealityKit

@main
struct HelloWorldApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

struct ContentView: View {
    var body: some View {
        VStack {
            Text("Hello, World!")
            
            // 在窗口中嵌入 3D 内容
            Model3D(named: "Satellite") { model in
                model
                    .resizable()
                    .aspectRatio(contentMode: .fit)
            } placeholder: {
                ProgressView()
            }
        }
    }
}

关键点:

  • Model3D 类似 Image,加载并显示由 RealityKit 渲染的 3D 模型
  • 窗口中的 3D 内容会沿 z 轴突出,增加深度感
  • 可以用 DragGesture 直接拖拽 3D 实体

创建 Volume

Volume 通过设置 windowStyle.volumetric 创建:

@main
struct EarthApp: App {
    var body: some Scene {
        WindowGroup(id: "earth") {
            EarthView()
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 0.6, height: 0.6, depth: 0.6, in: .meters)
    }
}

关键点:

  • .windowStyle(.volumetric) 把 WindowGroup 变成 Volume
  • defaultSize 指定体积的宽高深,单位可以是 .points.meters
  • Volume 内容必须保持在边界内
  • 适合 Shared Space,可以和其他窗口共存

RealityView 和 Attachments

RealityView 是在 SwiftUI 中管理 RealityKit 实体的核心视图:

import SwiftUI
import RealityKit

struct GlobeView: View {
    var body: some View {
        RealityView { content, attachments in
            // make closure: 创建实体并添加到根实体
            if let earth = try? await Entity(named: "Earth") {
                content.add(earth)
            }
            
            // 把 attachment 添加到场景中
            if let pin = attachments.entity(for: "pin") {
                pin.position = [0, 0.5, 0]
                content.add(pin)
            }
        } update: { content, attachments in
            // update closure: 状态变化时调用
        } attachments: {
            // attachments closure: 定义 SwiftUI 视图
            Attachment(id: "pin") {
                Image(systemName: "mappin")
                    .font(.largeTitle)
                    .foregroundColor(.red)
            }
        }
    }
}

关键点:

  • make closure 在视图创建时执行一次,用于初始化实体
  • update closure 在 SwiftUI 状态变化时调用
  • attachments 把 SwiftUI 视图转换为可以放在 3D 场景中的实体
  • 每个 attachment 需要一个唯一的 id

创建 Full Space

Full Space 通过 ImmersiveSpace Scene 类型创建:

@main
struct SpaceApp: App {
    @State private var immersiveStyle: ImmersionStyle = .full
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        
        ImmersiveSpace(id: "outerSpace") {
            OuterSpaceView()
        }
        .immersionStyle(selection: $immersiveStyle, in: .full, .mixed, .progressive)
    }
}

struct ContentView: View {
    @Environment(\.openImmersiveSpace) var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
    
    var body: some View {
        Button("Enter Space") {
            Task {
                await openImmersiveSpace(id: "outerSpace")
            }
        }
    }
}

关键点:

  • ImmersiveSpace 定义一个可进入的全空间场景
  • .immersionStyle 设置沉浸风格,默认是 .mixed
  • openImmersiveSpace 环境值用于打开空间
  • dismissImmersiveSpace 环境值用于关闭空间
  • 建议给用户选择是否进入沉浸式体验的按钮

手势与 3D 实体交互

SwiftUI 的手势可以直接 targeting RealityKit 实体:

Model3D(named: "Satellite") { model in
    model
        .resizable()
        .aspectRatio(contentMode: .fit)
}
.placeholder {
    ProgressView()
}
.gesture(
    DragGesture()
        .targetedToEntity(named: "Satellite")
        .onChanged { value in
            // 根据拖拽值移动实体
            value.entity.position = value.convert(value.location3D, from: .local, to: .parent)
        }
)

关键点:

  • .targetedToEntity(named:) 把手势绑定到特定实体
  • value.location3D 获取 3D 空间中的拖拽位置
  • convert(_:from:to:) 在坐标空间之间转换位置

从现有 App 迁移

visionOS 支持三种迁移路径(15:40):

  1. 兼容模式:iPad/iPhone App 无需修改即可运行,系统会自动处理窗口缩放和旋转
  2. 重新编译:在 Xcode 中添加 visionOS 目标,重新编译后获得原生间距、尺寸和重排
  3. 全新设计:使用 visionOS App 模板,从 Window 或 Volume 开始,充分利用空间计算能力

核心启发

用 Model3D 给现有 App 增加深度感

  • 做什么:在现有 SwiftUI App 的某个视图里加一个 Model3D,展示一个 3D 产品模型
  • 为什么值得做:不需要写任何 RealityKit 代码,Model3D 就像用 Image 一样简单,但能给界面增加空间深度
  • 怎么开始:把 USDZ 模型拖到 Xcode 项目中,用 Model3D(named: "模型名") 加载

做一个可旋转查看的 3D 商品展示

  • 做什么:用 Volume 展示一个 3D 商品模型,用户可以从各个角度查看
  • 为什么值得做:Volume 天生适合展示 3D 内容,用户可以用手势旋转、缩放,比 2D 图片直观得多
  • 怎么开始:创建 WindowGroup,设置 .windowStyle(.volumetric),在里面放 RealityView 加载商品模型

设计一个渐进式沉浸的冥想 App

  • 做什么:App 启动时在 Shared Space 显示控制面板,点击开始后进入 Full Space,用 Progressive 沉浸风格让用户自己调节沉浸程度
  • 为什么值得做:Progressive 沉浸让用户有控制权,从轻度沉浸逐步过渡到完全沉浸,减少不适感
  • 怎么开始:用 ImmersiveSpace 创建场景,设置 .immersionStyle(selection: $style, in: .progressive)

用 RealityView Attachments 做 3D 地图标记

  • 做什么:在 3D 地球仪或地图上,用 SwiftUI 视图作为标记点(pin)
  • 为什么值得做:Attachments 让你可以用熟悉的 SwiftUI 代码创建 3D 场景中的 UI 元素,不需要学习新的 UI 框架
  • 怎么开始:在 RealityViewattachments closure 里定义标记视图,在 make closure 里用 attachments.entity(for: "id") 获取并放置

关联 Session

评论

GitHub Issues · utterances