visionOS 多画面同时播放视频
Explore multiview video playback in visionOS
2024年6月10日
一句话判断
visionOS 的 Multiview 让用户同时看最多 5 个视频画面,而且系统帮你搞定了布局、光照效果和音频焦点切换——你只需要提供内容和浏览器 UI。
这场 Session 讲了什么
在 Vision Pro 上看视频已经是顶级体验了,但有些场景需要同时看多个画面:同时关注两场比赛、看几个主播的直播、或者多角度回放精彩片段。visionOS 今年推出的 Multiview 就是为此设计的。
Multiview 的核心思路是:每个视频画面背后是一个独立的 AVPlayerViewController,系统负责把它们排列成舒适的布局。用户可以通过一个 App 自定义的内容浏览器添加视频,点击切换焦点,拖拽调整位置。最多支持 5 个画面,系统会自动选择最优布局模板。
对开发者来说,关键要理解三个类的协作关系:AVPlayerViewController 播放单个视频,AVExperienceController 管理播放体验的切换(内嵌/展开/多画面),AVMultiViewManager 管理所有进入 Multiview 模式的画面。你要做的核心工作是实现一个自定义的浏览器 View Controller,提供内容选择 UI。
值得深挖的点
AVExperienceController:播放体验的状态机
Multiview 不是”打开一个多画面窗口”这么简单,它涉及到单画面和多画面两种体验之间的切换。AVExperienceController 就是管理这个切换的状态机。
每个 AVPlayerViewController 都有一个对应的 AVExperienceController,它定义了该播放器允许切换到哪些体验。默认支持 embedded(内嵌在 App 内容中)和 expanded(全屏展开)。今年新增了 multiview 体验。
当你在 allowedExperiences 里加入 .multiview,播放器的 UI 上就会出现 Multiview 按钮。用户点击后,AVExperienceController 会把播放器从当前体验过渡到 Multiview 模式。这个过渡过程是系统管理的,你不需要手动处理动画和布局。
反过来,当用户在浏览器里移除某个视频,你把对应的 AVExperienceController 切换回 .embedded 或其他体验,AVMultiViewManager 会自动把它从多画面布局中移除。如果这个播放器之前没有被安装到视图层级里,它会直接消失。
内容浏览器:你和系统的协作接口
内容浏览器是 Multiview 中唯一需要你深度定制的部分。它需要做两件事:管理可用的内容列表、提供内容选择的 UI。
把它设置到 AVMultiViewManager 上之后,系统会在用户点击 Multiview 按钮时自动展示浏览器。用户选择了内容后,你需要创建一个新的 AVPlayerViewController,设置好播放内容,把它的 AVExperienceController 切换到 Multiview 模式——然后系统接手后续的布局和渲染。
浏览器还负责响应移除操作。当用户通过浏览器移除一个视频,你把对应播放器的体验切换回非 Multiview 模式。但如果用户直接点击某个画面上的关闭按钮,这个事件不是来自浏览器的——你需要通过 AVExperienceController 的 delegate 来监听所有体验切换事件,统一处理。
代码片段
设置 Multiview 浏览器
// 自定义浏览器 View Controller
class ContentBrowserViewController: UIViewController {
var availableContent: [VideoItem] = []
// 用户选择内容后,添加到 Multiview
func didSelectContent(_ item: VideoItem) {
// 创建新的播放器
let playerVC = AVPlayerViewController()
playerVC.player = AVPlayer(url: item.url)
// 允许 Multiview 体验
let experience = playerVC.experienceController
experience.allowedExperiences.insert(.multiview)
// 过渡到正在进行的 Multiview 会话
experience.transition(to: .multiview)
}
}
// 在 App 启动时设置浏览器
let multiViewManager = AVMultiViewManager()
multiViewManager.browserViewController = ContentBrowserViewController()
场景:直播类 App 让用户同时看多个直播间。坑:新创建的 AVPlayerViewController 此时还没有视图在层级里,不要尝试访问它的 view。
监听体验切换事件
class ExperienceDelegate: NSObject, AVExperienceControllerDelegate {
func experienceController(
_ controller: AVExperienceController,
didUpdateTransition transition: AVExperienceController.Transition,
context: AVExperienceController.TransitionContext
) {
switch context.state {
case .started:
// 用户点击了 Multiview 按钮或关闭按钮
print("切换开始: \(transition)")
case .completed:
// 切换完成,更新 App 状态
updateAppState(for: transition)
default:
break
}
}
}
场景:无论是浏览器触发还是用户直接操作画面,都能统一捕获切换事件。坑:didUpdateTransition 会在每次状态变化时调用,包括中间状态,不要在非 completed 状态下做最终的状态更新。
单个播放器启用 Multiview 按钮
let playerVC = AVPlayerViewController()
playerVC.player = AVPlayer(url: streamURL)
// 添加 Multiview 到允许的体验列表
playerVC.experienceController.allowedExperiences = [.embedded, .expanded, .multiview]
// 设置 delegate 监听切换
playerVC.experienceController.delegate = experienceDelegate
场景:为现有播放器加上 Multiview 入口按钮。坑:必须同时支持 .embedded 或 .expanded 作为回退体验,否则用户无法退出 Multiview。
最佳实践
已有项目迁移:如果你的 visionOS App 已经用了 AVPlayerViewController,启用 Multiview 只需要三步:实现浏览器 View Controller、设置到 AVMultiViewManager、在播放器的 allowedExperiences 里加上 .multiview。不需要改动现有的单画面播放逻辑。
新项目起步:在架构视频播放模块时就把 AVExperienceController 作为播放体验的管理入口,而不是只拿 AVPlayerViewController 做播放。这样后续加 Multiview 支持时改动最小。浏览器 UI 的设计建议参考系统相册的多选交互——用户已经形成了这个心智模型。
还有什么值得关注
- Multiview 布局会自动处理光照效果——多个视频画面的光线会叠加投射到周围环境,比单个画面更沉浸。
- 音频焦点跟随用户点击的画面自动切换,音量是全局设置的,但播放速度是每个画面独立控制。
- 最多支持 5 个画面是经过人因工程测试的上限,不要试图绕过这个限制。