构建空间 SharePlay 体验
Build spatial SharePlay experiences
2023年6月5日
一句话判断
空间计算平台的 SharePlay 引入了”共享上下文”和”视觉一致性”两个核心概念——所有参与者看到相同的窗口位置和内容,手指指向的东西其他人也能看到。这不是简单的屏幕共享,是真正的”同处一室”体验。
这场 Session 讲了什么
Session 详细介绍了空间计算平台上 SharePlay 的新范式和 API。
新概念:
- Spatial Persona:参与者的虚拟形象占据物理空间,不再局限于窗口中。
- 共享上下文(Shared Context):所有人看到彼此和共享窗口的相同相对位置。如果 Connor 指向 Mia 的 Persona,Mia 看到 Connor 指向自己。
- 视觉一致性(Visual Consistency):所有人看到 App 中的相同内容。如果 Connor 指向方块,Mia 看到的也是 Connor 指向方块,而不是圆圈。
- 模板(Templates):系统使用模板确定参与者和 App 的空间排列方式。
三种模板:
- Side-by-Side:参与者在弧线上面向 App。默认模板,适合常规窗口 App。
- Conversational:参与者坐半圆,App 在前方。适合音乐播放等非内容聚焦场景。
- Surround:参与者围成圆,App 在圆心。只适用于 Volume(三维内容)场景。
System Coordinator:
新 API SystemCoordinator 负责两件事:
- 接收系统状态(参与者是否为 spatial)。
- 提供配置选项(模板偏好、isSpatial 标志)。
isSpatial 标志: 决定是否需要同步内容位置。当参与者是 spatial 时(有空间上下文),需要同步滚动位置等;当不是 spatial 时(如普通 FaceTime),不同步这些操作。
场景关联(Scene Association): 多窗口 App 需要告诉系统哪个窗口承载 SharePlay 活动。单窗口 App 自动处理,多窗口 App 如果不设置会随机选择一个窗口。
值得深挖的点
视觉一致性的责任在 App 端。系统提供了空间定位和模板排列,但 App 内部的内容同步需要开发者自己实现。Freeform 的示例展示了”同步滚动位置”的具体做法——当 Connor 滚动文档时,Mia 的文档也同步滚动。这种同步不是自动的,需要通过 GroupSession 的数据通道传递操作指令。
模板选择影响整个体验的空间感。Side-by-Side 适合内容消费类 App(一起看视频),Conversational 适合内容不是焦点的场景(一起听音乐聊天),Surround 适合 3D 内容的协作场景。选错模板会让空间排列感觉不自然——比如用 Surround 放一个 2D 文档窗口会让参与者围着一张纸站圈,很奇怪。
isSpatial 的条件性同步是一个需要仔细处理的细节。同样是 SharePlay 会话,参与者可能有人用空间计算设备(isSpatial = true),有人用普通 iPhone(isSpatial = false)。你的 App 需要根据这个标志决定同步策略——spatial 参与者需要位置同步,非 spatial 参与者不需要。
代码片段
获取 System Coordinator 并配置模板:
import GroupActivities
// 获取 group session 的 system coordinator
let coordinator = await session.systemCoordinator
// 观察 isSpatial 状态
for await state in coordinator.localParticipantState {
if state.isSpatial {
// 参与者有空间上下文,需要同步内容位置
enableContentSync()
} else {
// 普通参与者,不需要空间同步
disableContentSync()
}
}
// 配置模板偏好(在 join 之前设置)
var config = SystemCoordinator.Configuration()
config.templatePreference = .conversational // 适合音乐类 App
coordinator.setConfiguration(config)
场景关联(多窗口 App):
// 多窗口 App 需要指定哪个窗口承载 SharePlay
// 单窗口 App 自动处理
// 在 group session 的配置中指定关联的场景
session.join(options: .init(
// 告诉系统哪个 scene 是 SharePlay 的主场景
associatedScene: detailWindowScene
))
// 系统会在模板中使用这个指定的窗口
// Share 菜单也会标注哪个窗口正在被共享
条件性内容同步:
// 根据 isSpatial 决定是否同步滚动位置
func handleScrollUpdate(_ position: CGPoint) {
guard let coordinator = systemCoordinator else { return }
Task {
for await state in coordinator.localParticipantState {
if state.isSpatial {
// 空间参与者:同步滚动位置
// 这样当有人指向内容时,其他人能看到指向的位置
broadcastScrollPosition(position)
}
// 非 spatial 参与者:不同步滚动
break
}
}
}
最佳实践
- 空间 SharePlay 的 App 必须实现视觉一致性——所有参与者看到相同的内容排列。
- 根据你的 App 类型选择合适的模板:内容消费用 Side-by-Side,音乐/背景体验用 Conversational,3D 内容用 Surround。
- 多窗口 App 一定要设置场景关联,否则系统可能选错共享窗口。
- 用 isSpatial 标志决定是否同步内容位置,避免非空间参与者看到意外的 UI 变化。
- 单窗口 App 不需要处理场景关联,系统自动管理。
- 模板偏好只在 SharePlay 会话活跃时生效,会话结束后恢复正常布局。
还有什么值得关注
- “Design spatial SharePlay experiences” Session 详细介绍了共享上下文和视觉一致性的设计原则。
- Spatial Persona 是空间计算平台独有的概念,普通 FaceTime 不适用。
- 模板会根据 App 的大小自动调整参与者距离——大 App 远一些,小 App 近一些。
- Share 菜单会标注哪个窗口正在被共享,避免用户意外操作共享窗口的尴尬。
- Surround 模板只适用于 Volume 场景,2D 窗口场景只能用 Side-by-Side 或 Conversational。