Highlight
Spatial Preview 框架让 macOS 应用零 visionOS 代码即可将文档和 3D 内容推送到 Vision Pro 的 Quick Look 中,支持双向实时编辑和 SharePlay 协作。
核心内容
从”再写一套 visionOS App”到”几行代码搞定”
做 3D 内容工具的开发者都经历过这个场景:Mac 上建好了精美的建筑模型或材质,想让客户在 Vision Pro 里沉浸式查看,得再写一个 visionOS 伴侣 App,搞定网络传输、场景同步、手势交互。两套代码、两套资产管线、两套发布流程。
(00:27)Spatial Preview 框架直接把这个流程砍到了 Mac 端几行代码。你的 Mac App 创建一个 Session,把文件 URL 或 USD Stage 塞进去,Vision Pro 上的 Quick Look 自动接管渲染和交互。不需要写任何 visionOS 代码,不需要处理设备配对,不需要自己搭网络同步层。
Cinema 4D 和 SketchUp 已经接入了这个能力,实现了材质修改的实时双向同步(01:43)。你在 Mac 上调一下金属度参数,Vision Pro 里的模型立刻跟着变。
两种 Session 覆盖两种场景
框架提供了两类 Session:
- DocumentPreviewSession:处理空间照片、空间视频、Apple Immersive Video 帧、PDF 等文档类型。
- USDPreviewSession:处理 3D 内容,基于 USDKit 的 USD Stage 做双向实时编辑。
(03:07)选择哪种取决于你的内容类型。做视频剪辑工具的用 DocumentPreviewSession,做 3D 建模或 CAD 工具的用 USDPreviewSession。
设备发现:别假设 Mac Virtual Display 永远在线
框架第一步是获取目标设备端点(Endpoint)。如果用户正在使用 Mac Virtual Display,可以直接拿到已连接的设备。但如果没连,你的 App 需要提供一个设备选择器让用户手动挑选同一 iCloud 账号下的 Vision Pro。
(04:20)Apple 提供了现成的 SpatialPreviewDevicePicker SwiftUI 视图,用 Sheet 弹出来就行。不要假设用户永远开着 Mac Virtual Display,否则你的”预览”按钮在没连头显时就是个死按钮。
自动优化的双刃剑
(08:32)Vision Pro 的算力虽然强,但渲染 Mac 端几百万面的工业级 USD 场景还是会卡。Spatial Preview 默认开启自动优化,偷偷做网格减面(Mesh Decimation)和纹理降采样,保证帧率。
这对普通预览很友好,但做高精度工业评审的开发者需要注意:减面会导致细节丢失。你可以传 .unmodified 参数关闭优化,但如果场景太复杂,start 方法会直接抛出 assetUnshareable 错误。这意味着你必须在 Mac 端自己做一层资产检查或 LOD 管理,不能无脑把原始资产扔给框架。
SharePlay 协作开箱即用
(13:05)Vision OS 端的 SharePlay 支持是内置的。多个协作者可以加入同一个 Live Session,同时查看和编辑同一份空间内容。一个人在 Vision Pro 里移动家具位置,所有人的视图立刻同步更新。
详细内容
启动文档预览 Session
(03:58)以下代码展示如何创建 Document Preview Session,通过 Mac Virtual Display 推送到 Vision Pro:
import SwiftUI
import SpatialPreview
let deviceObserver = ConnectedSpatialEndpointObserver()
let previewSession = DocumentPreviewSession(name: "Immersive.aivu", contentType: .aivu)
func startPreview(contentURL: URL, endpoint: SpatialPreviewEndpoint) async throws {
let endpoint = try await deviceObserver.endpoint
try await previewSession.start(endpoint: endpoint)
try await previewSession.updateContents(url: contentURL)
}
@State var showDevicePicker: Bool = false
var body: some View {
// ...
.sheet(isPresented: $showDevicePicker) {
SpatialPreviewDevicePicker(isPresented: $showDevicePicker) { endpoint in
showDevicePicker = false
Task {
try await startPreview(filename: filename, endpoint: endpoint)
}
}
}
}
关键点:
ConnectedSpatialEndpointObserver()监听当前 Mac Virtual Display 连接的设备,返回一个SpatialPreviewEndpoint。DocumentPreviewSession需要指定内容类型(.aivu表示 Apple Immersive Video)和会话名称。start(endpoint:)启动会话后,Vision Pro 的 Quick Look 自动打开。updateContents(url:)将内容推送到已打开的会话中,复用同一场景。SpatialPreviewDevicePicker是系统提供的设备选择 UI,用户可从附近同一 iCloud 账号的 Vision Pro 中选择。
更新内容并监听会话状态
(05:20)做画廊切换时,用 updateContents 复用场景,而不是每次都新建 Session:
import SwiftUI
import SpatialPreview
ForEach(contentURLs, id: \.self) { url in
Button {
Task { try await previewSession?.updateContents(url: url) }
}
}
.task(id: previewSession.map { ObjectIdentifier($0) }) {
for await state in Observations({ session.state }) {
if state.isInvalidated {
previewSession = nil
break
}
}
}
try await previewSession?.close()
关键点:
updateContents(url:)复用当前场景,避免 Vision Pro 上场景闪烁重建。- 如果每次切换文件都重新调用
start,会启动新场景,体验很差。 - 通过
Observations监听session.state,当用户在 Vision Pro 关闭 Quick Look 时,isInvalidated变为 true,Mac 端可以清理 UI 状态。 - 会话结束时调用
close(),Vision Pro 上的场景自动关闭。
启动 USD 实时预览
(07:36)3D 内容使用 USDKit 加载 USD 场景,再通过 USDPreviewSession 推送:
import SpatialPreview
import USDKit
let deviceObserver = ConnectedSpatialEndpointObserver()
var usdSession: USDPreviewSession?
func shareStage(to endpoint: SpatialPreviewEndpoint) async throws -> USDPreviewSession {
let endpoint = try await deviceObserver.endpoint
let stageURL = Bundle.main.url(forResource: "sampleScene", withExtension: "usdz")
let stage = try USDStage.open(stageURL)
usdSession = USDPreviewSession(stage: stage)
try await usdSession?.start(endpoint: endpoint)
}
关键点:
USDStage.open(stageURL)用 USDKit 加载本地 USDZ 文件。USDPreviewSession(stage:)将 Stage 包装成预览会话。start(endpoint:)启动后,Vision Pro 以体积视图(Volumetric View)展示 3D 内容,用户可切换为沉浸视图(Immersive View)。- 相机视角、线框材质覆盖等功能由 Vision Pro 端的 Quick Look 内置提供,Mac 端无需额外代码。
关闭自动优化并处理错误
(08:56)高精度场景需要关闭自动优化:
import SpatialPreview
let endpoint = try await deviceObserver.endpoint
do {
try await usdSession.start(endpoint: endpoint, parameters: .unmodified)
} catch USDPreviewSession.Error.assetUnshareable {
// Handle Asset Unshareable error
}
关键点:
.unmodified参数关闭网格减面和纹理降采样,保留原始精度。- 如果场景复杂度超过 Vision Pro 的承载能力,
start抛出assetUnshareable错误。 - 需要在 Mac 端捕获这个错误,给用户降级选项(如加载低模版本)。
使用 USD Variants 切换布局
(10:10)USD 的 Variant Sets 可以在 Mac 和 Vision Pro 之间实时同步:
# LayoutVariants.usda
#usda 1.0
over "furniture" (
variantSets = "Layout"
variants = { string Layout = "LayoutA" }
)
{
variantSet "Layout" = {
"LayoutA" {
// Default furniture position and rotation
}
"LayoutB" {
// Moves furniture prims to a different position and rotation
}
...
}
}
import SpatialPreview
import USDKit
func applyLayoutVariant(named layoutVariantName: String) throws {
let prim = stage.prim(at: SdfPath("/root/furniture"))
try prim.variantSets?.setSelection("Layout", variantName: layoutVariantName)
}
关键点:
- Variant Sets 是 USD 的标准机制,允许在同一 Prim 上切换不同数据变体。
prim.variantSets?.setSelection切换变体后,Stage 的改动自动同步到 Vision Pro。- 在 Vision Pro 的 Quick Look 菜单中也可以切换变体,改动同样回传到 Mac。
- 适合做室内设计 App,让用户在”现代风”和”原木风”布局间一键切换。
监听 Stage 变化与批注
(10:49)监听 Vision Pro 端对 USD Stage 的修改:
import SpatialPreview
import USDKit
let observerToken: ObservationToken
observerToken = stage.addObserver(for: UsdStage.ObjectsDidChange.self) { notice in
for path in notice.resyncedPaths {
let prim = notice.stage.prim(at: path)
guard prim.isValid else { continue }
if prim.isAnnotation {
// Handle annotation change
break
}
}
}
(11:13)批注数据需要符合特定 Schema:
AppleTextAnnotation {
string text
uniform string author
uniform string identifier
}
/__documentAnnotationGroup__
关键点:
stage.addObserver(for: UsdStage.ObjectsDidChange.self)订阅 Stage 变化通知。notice.resyncedPaths包含所有发生变化的 Prim 路径。prim.isAnnotation判断该 Prim 是否为批注。- 批注必须作为
__documentAnnotationGroup的子 Prim,并符合AppleTextAnnotationSchema,才能在 Vision Pro 上正确显示。 - 批注包含
text(内容)、author(作者标识)和identifier(唯一标识符)三个字段。
启用对象操作与事件监听
(11:33)让 Vision Pro 用户能用手势移动 3D 对象,需要在 USD 中设置元数据:
customData = {
dictionary apple = {
bool spatialEditable = 1
}
}
(12:16)配置 Session 选项并监听事件:
import SpatialPreview
import USDKit
session.start(endpoint: endpoint, options: [.annotations, .perObjectManipulation, .export])
func listenForEvents(session: USDPreviewSession) async {
for await event in session.events {
if case .timeChanged(let time) = event {
playbackModel.timeCode = time
} else if case .playbackStateChanged(let isPlaying) = event {
playbackModel.playbackStateChanged(isPlaying)
}
}
}
关键点:
spatialEditable = 1的元数据标记让 Prim 在 Vision Pro 上支持手势拖拽移动。options: [.annotations, .perObjectManipulation, .export]启用批注、对象操作和导出功能,默认全部开启。session.events提供动画播放时间变化(timeChanged)和播放状态变化(playbackStateChanged)等事件。- 可以据此在 Mac 端同步播放控制 UI。
监控同步进度
(12:38)大文件传输时展示进度:
import SpatialPreview
import USDKit
@State private var sessionProgress: Double = 0
var body: some View {
// ...
.task(id: usdSession.map { ObjectIdentifier($0) }) {
guard let session = usdSession else { return }
for await fraction in Observations({ session.progress.fractionCompleted }) {
sessionProgress = fraction
}
}
.overlay(alignment: .bottom) {
ProgressView(value: sessionProgress)
.padding()
}
}
关键点:
session.progress.fractionCompleted返回 0 到 1 的同步进度。- 通过
Observations监听进度变化,实时更新 UI。 - 传输几百 MB 的 USDZ 文件时,进度条比转圈动画更能安抚用户。
核心启发
1. 给现有 Mac 3D 工具加一个”空间预览”按钮
做什么:在现有 Mac 建模或设计工具的工具栏加一个”在 Vision Pro 中预览”按钮。
为什么值得做:不需要重写任何 visionOS 代码,几行 Spatial Preview API 就能让产品具备”空间计算”卖点。客户可以在头显里以真实比例查看模型,比屏幕上的 3D 视图直观得多。
怎么开始:引入 SpatialPreview 和 USDKit 框架,用 ConnectedSpatialEndpointObserver 获取设备,创建 USDPreviewSession,把当前打开的 USD Stage 传进去。
2. 做一个支持实时协作的室内设计评审工具
做什么:Mac 端放平面图和家具库,Vision Pro 端展示 3D 房间。设计师和客户同时查看,实时讨论布局调整。
为什么值得做:SharePlay 支持是内置的,不需要自己实现多人同步。USD Variants 可以预置几套布局方案,一键切换对比。
怎么开始:用 USDPreviewSession 的 options: [.annotations, .perObjectManipulation] 启用批注和物体移动。在 Mac 端监听 ObjectsDidChange 通知,把 Vision Pro 上的批注和位置改动存进自己的项目数据库。
3. 给视频编辑工具加”空间帧预览”
做什么:从 Apple Immersive Video 中提取关键帧,一键推送到 Vision Pro 查看立体效果。
为什么值得做:DocumentPreviewSession 的 .aivu 类型直接支持 Apple Immersive Video 帧。视频剪辑师可以在剪辑过程中随时检查空间效果,不用导出完整视频再传输到头显。
怎么开始:用 DocumentPreviewSession(name:contentType: .aivu) 创建会话,调用 updateContents(url:) 推送帧文件。画廊视图用 updateContents 复用场景做快速切换。
4. 做一套 USD 资产的”空间批注”工作流
做什么:3D 模型评审时,评审人在 Vision Pro 里直接在模型上钉批注,Mac 端自动收集整理。
为什么值得做:批注以标准 USD Prim 形式存在,不依赖私有格式。你的 Mac App 可以按 author 和 identifier 字段把批注归类,生成评审报告。
怎么开始:确保批注 Prim 符合 AppleTextAnnotation Schema,放在 __documentAnnotationGroup 下。Mac 端用 stage.addObserver(for: UsdStage.ObjectsDidChange.self) 监听变化,提取批注数据。
5. 在 Mac 端做性能预警,避免推到 Vision Pro 后报错
做什么:在调用 start 之前,先分析 USD Stage 的面数和纹理大小,给用户一个”此模型可能在 Vision Pro 上性能不佳”的提示。
为什么值得做:关闭自动优化后复杂场景会直接抛 assetUnshareable 错误,体验很差。提前在 Mac 端做检查,可以给用户加载简化版的选项。
怎么开始:用 USDKit 遍历 Stage 中的所有 Mesh Prim,累加面数;检查纹理尺寸。超过阈值时提示用户选择低模版本或开启自动优化。
关联 Session
- Discover the USDKit framework — USDKit 是 Spatial Preview 处理 3D 内容的基础,了解 USD Stage、Prim 和 Layer 的概念对使用 Spatial Preview 至关重要。
- What’s new in RealityKit — RealityKit 是 Vision Pro 上渲染 3D 内容的底层框架,Spatial Preview 的 Quick Look 视图基于 RealityKit 构建。
- Discover Reality Composer Pro 3 — Reality Composer Pro 3 支持在 Mac 上编辑 USD 场景并设置
spatialEditable元数据,与 Spatial Preview 的工作流直接衔接。 - Build immersive environments in visionOS — 了解 visionOS 的沉浸视图和体积视图概念,有助于设计 Spatial Preview 内容的展示方式。
- What’s new in visionOS — visionOS 27 的新特性为 Spatial Preview 提供了 Mac Virtual Display 和 SharePlay 等底层支持。
评论
GitHub Issues · utterances