WWDC Quick Look 💓 By SwiftGGTeam
Explore multiview video playback in visionOS

Explore multiview video playback in visionOS

观看原视频

Highlight

Multiview 让用户在 Vision Pro 上最多同时播放 5 个视频画面,系统自动管理布局模板,每个画面由独立的 AVPlayerViewController 驱动。


核心内容

想象这样一个场景:你在看体育比赛,同时想关注另一场正在进行的比赛;或者你在看几个主播的直播,想同时看到所有画面;再或者内容本身提供多机位视角,你想切换不同角度。这些场景的核心需求是:同时看多个画面,并且能在它们之间切换焦点。

visionOS 今年推出的 Multiview 就是为此设计的解决方案。它的核心思路很直接:每个视频画面背后是一个独立的 AVPlayerViewController,系统负责把它们排列成舒适的布局。用户通过一个 App 自定义的内容浏览器添加视频,点击画面切换焦点,拖拽调整位置。

Multiview 支持最多 5 个画面同时播放,系统会根据画面数量自动选择最优布局模板。用户可以添加/移除画面、重新排列、调整大小。其中某个画面处于”主焦点”状态时,它的音频会优先播放,控制条会响应这个画面的操作。某些设置(如音量)对所有画面生效,而某些设置(如播放速度)只影响当前画面。

对开发者来说,关键要理解三个类的协作关系:AVPlayerViewController 播放单个视频,AVExperienceController 管理播放体验的切换(内嵌/展开/多画面),AVMultiViewManager 管理所有进入 Multiview 模式的画面。你需要实现的核心工作是:提供一个自定义的浏览器 View Controller,提供内容选择 UI。


详细内容

Multiview 的实现依赖三个核心类的协作。首先,每个视频画面由一个独立的 AVPlayerViewController 驱动。这个控制器有一个新的配对类:AVExperienceController。体验控制器定义了播放器可以在哪些体验模式之间切换:内嵌(embedded,视频与应用内容并排显示)、展开(expanded,视频屏幕单独显示)、多画面(multiview)。

要让某个播放器支持 Multiview,需要在它的体验控制器的 allowedExperiences 中加入 .multiView。这会在播放器 UI 上显示一个多画面按钮。用户点击时,体验控制器会切换到多画面体验,这时会显示内容选择浏览器,表明体验已经开始。

内容选择浏览器是 App 自定义的 View Controller,负责管理可用内容和提供选择 UI。需要把它附加到 AVMultiViewManager。这个管理器负责很多细节:在需要时激活浏览器 UI,追踪哪些体验控制器进入了多画面模式,维护视频屏幕的布局。

浏览器视图控制器和多画面管理器协作来添加或移除内容。当用户通过浏览器 UI 选择额外内容时,信号让浏览器视图控制器创建另一个 AVPlayerViewController 来播放该内容。使用它的体验控制器切换到进行中的多画面体验。多画面管理器追踪进入多画面的体验控制器集合,并相应更新视频屏幕布局。

下面是设置代码。

设置内容浏览器

把自定义的浏览器视图控制器设置到多画面管理器(07:47):

import AVKit

AVMultiViewManager
    .default
    .contentSelectionViewController = multiViewController()

关键点

  • AVMultiViewManager.default 是单例,管理整个应用的多画面体验
  • contentSelectionViewController 需要尽早设置,这样管理器才能在需要时激活浏览器 UI
  • 这个视图控制器负责展示可用内容,处理用户选择

添加内容到多画面

当用户选择内容后,创建新的播放器并加入多画面(08:09):

import AVKit

let controller = AVPlayerViewController()

let experienceController = controller.experienceController
experienceController.allowedExperiences = .recommended(including: [.multiView])

await experienceController.transition(to: .multiView)

关键点

  • 创建 AVPlayerViewController 但此时视图还未加入视图层级
  • experienceController 初始处于平台默认的体验模式
  • .recommended(including:) 会包含推荐的体验加上指定的额外体验
  • transition(to:) 方法会让管理器接管,把视频加入多画面布局

从多画面移除内容

当用户通过浏览器移除内容时,把体验切换到其他模式(08:47):

import AVKit

let experienceController =

await experienceController.transition(to: .embedded)

关键点

  • 移除时体验控制器已经在多画面模式中
  • 切换到 .embedded 会把视频从多画面布局中移除
  • 如果对应的播放器视图控制器之前没安装在视图层级中,管理器会直接让它消失

处理浏览器外的事件

除了内容浏览器,用户还可以通过其他方式与多画面交互,比如点击视频屏幕本身、通过播放控制退出多画面。这些事件需要通过 AVExperienceController 的 delegate 来处理(09:19)。

delegate 接收所有转换事件,无论它们如何发生,并且包含关于每个转换进度的上下文。当单屏幕播放器的多画面按钮被点击时,体验控制器会切换到多画面,delegate 会收到通知,App 可以做出反应。用户点击视频屏幕或通过播放控制完全退出多画面时,delegate 也会收到这些转换事件。

delegate 还有机会在转换开始前完成额外工作(10:11)。这是 App 准备状态、显示或隐藏相关 UI、为流畅转换做准备的机会。

最佳实践

渐进式体验:多画面是一个强大的功能,但对不期待它的用户来说可能不知所措。支持优雅的渐进式体验是关键。从熟悉的单画面观看开始,为想更进一步的人建议多画面。使用 AVPlayerViewController 提供的标准按钮。探索多画面选项后,有人可能仍然偏好单画面,这种情况下也要提供同样好的体验(10:46)。

内容选择:多画面适合观看同时进行的内容,主要强调其中一条。不适合像连续观看电视剧剧集这种情况,后者应考虑 AVQueuePlayer11:21)。如果没有强烈的使用场景,考虑探索其他 AVKit 选项来获得合适的体验。

内容浏览器设计:内容浏览器是添加和移除内容的主要手段。它应该直观,让人知道期待什么,能快速看到想看的内容。用标题和缩略图预览简洁地描述每条内容(12:10)。浏览器区域相对视频屏幕有限,尽量减少装饰,专注于便于识别的元素。整体风格与体验的其他部分匹配,比如缩略图的长宽比应与内容播放时匹配,适当时匹配强调色。不要过度强调浏览器或它的选择,这会阻碍整体体验。

突出当前选择。一个人可能只观看可用内容的一小部分,帮助他们在浏览器中识别哪些内容在屏幕上(12:46)。但要记住,过于激进地在浏览器中重新排列内容,或拥挤有限的空间,可能阻碍人们导航和选择的能力。


核心启发

  1. AVExperienceController 作为播放体验的管理入口。在架构视频播放模块时,不要只拿 AVPlayerViewController 做播放,而要把体验控制器作为体验管理的中心。这样后续加 Multiview 或其他新体验模式时改动最小。

    为什么值得做:Apple 在 visionOS 上持续扩展播放体验(如今年的 Multiview、沉浸式环境),以体验控制器为中心的架构能让你更快适配新特性。

    怎么开始:在现有播放模块中访问 playerViewController.experienceController,用 allowedExperiences 明确支持的体验模式。新项目直接在设计时就把体验控制器作为抽象层。

  2. 为 Multiview 设计轻量级内容浏览器。参考系统相册的多选交互——用户已经形成了这个心智模型。浏览器要简洁、直观,缩略图长宽比与实际视频匹配,减少装饰元素。

    为什么值得做:浏览器是用户添加/移除多画面内容的主要入口,设计不当会让强大的功能变得难用。直观的浏览器能降低多画面的认知负担。

    怎么开始:实现一个继承 UIViewController 的浏览器类,用列表或网格展示可用内容。每个项包含标题和缩略图。在用户选择时调用 AVMultiViewManager 添加或移除内容。参考视频中的示例代码。

  3. 用 delegate 保持应用状态与多画面管理器同步。当用户通过点击视频屏幕或播放控制来操作时,这些事件不经过浏览器,需要通过体验控制器的 delegate 处理。

    为什么值得做:用户有多种方式与多画面交互,只监听浏览器事件会导致状态不一致。delegate 能让你对任何转换事件做出反应。

    怎么开始:为体验控制器设置 delegate,实现 experienceController(_:willTransitionTo:completion:) 等方法。在这些方法中更新应用状态、显示或隐藏 UI、准备资源。


关联 Session

评论

GitHub Issues · utterances