Highlight
Apple 在 tvOS 15 重新设计 AVPlayerViewController 标准播放界面,开发者可以把标题、播放控制、内容标签和上下文操作接入系统播放器,减少自定义播放 UI 的维护成本。
核心内容
做 tvOS 媒体 App,最难的部分常常不在播放视频本身。
用户拿着遥控器,想打开字幕、切换音轨、收藏当前节目、跳过片头、查看章节。以前如果只用基础播放器,这些入口要散落在自定义菜单里。你还要处理遥控器手势、焦点移动、画中画、Siri 指令和未来系统行为。
这类自定义播放器很容易和系统播放界面打架。演讲里明确建议,不要给播放器额外添加手势,因为它会干扰标准播放 UI,并且未来可能失效。
Apple 在 tvOS 15 重新设计了标准播放界面。AVPlayerViewController 继续负责播放、跳转、扫描、拖动、Siri 指令、Interstitials(插播内容)和遥控器支持。开发者在它上面补充应用自己的信息和操作。
新的入口集中在四个地方。
Title View 显示当前内容的标题和副标题。Transport Bar Controls 把收藏、设置等操作放到进度条上方。Content Tabs 在播放界面内展示信息、章节和推荐内容。Contextual Actions 在特定时间段显示“跳过片头”或“回顾前情”这类操作。
用户不用离开播放器。开发者也不用重新实现一套播放器外壳。
要使用这些新界面,App 必须链接 tvOS 15 SDK。旧 App 会继续得到 tvOS 14 的默认播放界面,这是 Apple 用来保持兼容性的边界。
详细内容
Title View:让播放器知道当前在播什么
(03:08)
Title View 位于 transport bar(播放控制条)上方。它从当前 AVPlayerItem 的 metadata(元数据)读取标题和副标题。
演讲提到两个字段:commonIdentifierTitle 和 iTunesMetadataTrackSubtitle。如果媒体文件没有内嵌元数据,或者你需要补充信息,可以设置 AVPlayerItem.externalMetadata。
import AVFoundation
import AVKit
func configureTitleView(
playerItem: AVPlayerItem,
playerViewController: AVPlayerViewController,
title: String,
subtitle: String
) {
let titleItem = AVMutableMetadataItem()
titleItem.identifier = .commonIdentifierTitle
titleItem.value = title as NSString
let subtitleItem = AVMutableMetadataItem()
subtitleItem.identifier = .iTunesMetadataTrackSubtitle
subtitleItem.value = subtitle as NSString
playerItem.externalMetadata = [titleItem, subtitleItem]
playerViewController.transportBarIncludesTitleView = true
}
关键点:
import AVFoundation引入AVPlayerItem和 metadata 类型。import AVKit引入AVPlayerViewController。configureTitleView接收当前播放项和播放器控制器。AVMutableMetadataItem()创建可写元数据项。titleItem.identifier = .commonIdentifierTitle对应演讲中用于 Title View 的标题字段。titleItem.value = title as NSString写入标题文本。subtitleItem.identifier = .iTunesMetadataTrackSubtitle对应演讲中的副标题字段。subtitleItem.value = subtitle as NSString写入副标题文本。playerItem.externalMetadata用外部元数据补齐媒体信息。transportBarIncludesTitleView = true保持 Title View 显示;如果不想显示,可以设为false。
Transport Bar Controls:把常用操作放进系统播放控制条
(04:01)
Transport Bar Controls 是进度条上方的一组控件。系统会提供字幕、音频语言、画中画等标准控件。
应用自己的操作通过 AVPlayerViewController.transportBarCustomMenuItems 添加。它支持 UIAction 和 UIMenu。演讲还说明,AVPlayerViewController 展示的 UIMenu 支持一层嵌套,并可使用 displayInline 选项。
import AVKit
import UIKit
func configureTransportBarControls(playerViewController: AVPlayerViewController) {
let favoriteAction = UIAction(
title: "Favorites",
image: UIImage(systemName: "heart")
) { _ in
// Add to favorites
}
let normalSpeedAction = UIAction(title: "Normal", state: .on) { _ in }
let fastSpeedAction = UIAction(title: "Fast") { _ in }
let speedMenu = UIMenu(
title: "Speed",
options: [.displayInline, .singleSelection],
children: [normalSpeedAction, fastSpeedAction]
)
let settingsMenu = UIMenu(
image: UIImage(systemName: "gearshape"),
children: [speedMenu]
)
playerViewController.transportBarCustomMenuItems = [
favoriteAction,
settingsMenu
]
}
关键点:
import AVKit提供AVPlayerViewController。import UIKit提供UIAction、UIMenu和UIImage。favoriteAction创建一个收藏操作,对应演讲中的 Favorites 示例。UIImage(systemName: "heart")给收藏操作加系统图标。- handler 里的注释位置执行“加入收藏”的业务逻辑。
normalSpeedAction和fastSpeedAction是菜单子项。state: .on标记当前选中的菜单项。speedMenu创建 Speed 菜单,对应演讲中的 Speed 子菜单。.displayInline让子菜单以内联方式展示。.singleSelection表示同一组里只选一个操作。settingsMenu使用齿轮图标承载子菜单。transportBarCustomMenuItems把自定义操作交给系统播放界面展示。
Content Tabs:播放时展示信息、章节和推荐内容
(05:19)
Content Tabs 让播放界面承载补充信息。媒体有 metadata 时,AVPlayerViewController 会显示 Info 标签。AVPlayerItem 提供 navigationMarkerGroups 时,系统会显示 Chapters 标签。
应用可以通过 customInfoViewControllers 添加更多标签。tvOS 15 用这个新属性替代旧的 customInfoViewController,因为新的接口支持多个 view controller。
import AVKit
import UIKit
func configureContentTabs(
playerViewController: AVPlayerViewController,
customViewController: UIViewController
) {
customViewController.preferredContentSize = CGSize(width: 0, height: 140)
customViewController.title = "Recommended"
playerViewController.customInfoViewControllers = [customViewController]
}
关键点:
configureContentTabs接收播放器控制器和自定义内容控制器。preferredContentSize指定内容标签需要的高度。width: 0表示这里重点设置高度,宽度由系统布局处理。height: 140对应演讲代码片段中的示例高度。title = "Recommended"设置标签标题,系统会用它显示 tab 名称。customInfoViewControllers添加自定义内容标签数组。- 多个标签同时存在时,系统会按最高的内容标签计算整体高度。
TVMediaItemContentConfiguration:用系统样式展示推荐内容
(07:08)
如果 Content Tabs 里要展示一组相关视频,可以用 TVUIKit(tvOS UI 工具框架)提供的新内容配置。
TVMediaItemContentConfiguration.wideCell() 用于 16:9 媒体卡片。它能设置图片、标题、副标题、徽章和播放进度。
import TVUIKit
import UIKit
func configureMediaCell(cell: UICollectionViewCell) {
var contentConfiguration = TVMediaItemContentConfiguration.wideCell()
contentConfiguration.image = UIImage(imageLiteralResourceName: "tanu")
contentConfiguration.text = "Title"
contentConfiguration.secondaryText = "Secondary text"
contentConfiguration.badgeText = "NEW"
contentConfiguration.badgeProperties.backgroundColor = .systemRed
contentConfiguration.playbackProgress = 0.75
cell.contentConfiguration = contentConfiguration
}
关键点:
import TVUIKit引入 tvOS 的内容配置类型。import UIKit引入UICollectionViewCell和UIImage。configureMediaCell配置一个 collection view cell。TVMediaItemContentConfiguration.wideCell()创建 16:9 媒体卡片配置。image设置卡片图片。text设置主标题。secondaryText设置副标题。badgeText = "NEW"给卡片显示徽章文字。badgeProperties.backgroundColor = .systemRed设置徽章背景色。playbackProgress = 0.75显示 75% 的观看进度。cell.contentConfiguration把配置应用到 cell。
TVMonogramContentConfiguration:在播放界面展示人物
(07:36)
如果内容标签展示的是人物或角色,演讲建议使用 TVMonogramContentConfiguration。
它适合在当前媒体相关区域展示演员、嘉宾、讲者或角色信息。
import TVUIKit
import UIKit
func configureMonogramCell(cell: UICollectionViewCell) {
var contentConfiguration = TVMonogramContentConfiguration.cell()
contentConfiguration.image = UIImage(imageLiteralResourceName: "jad")
contentConfiguration.text = "Jad"
cell.contentConfiguration = contentConfiguration
}
关键点:
TVMonogramContentConfiguration.cell()创建人物头像样式的 cell 配置。image设置人物图片。text设置人物名称。cell.contentConfiguration让 collection view cell 使用这套系统样式。- 这个配置和媒体卡片配置都服务于 Content Tabs 内的列表内容。
Contextual Actions:只在合适时间显示操作
(07:52)
Contextual Actions 用于播放过程中的即时操作。演讲给出的例子是 Skip Intro 和 Recap。
做法是设置 AVPlayerViewController.contextualActions,提供一个或多个 UIAction。应用通常需要监听播放器时间,在相关片段出现时显示操作,结束后把属性重置为空数组。
import AVFoundation
import AVKit
import UIKit
func showSkipIntroAction(
playerViewController: AVPlayerViewController,
player: AVPlayer
) {
let skipIntroAction = UIAction(title: "Skip Intro") { _ in
let targetTime = CMTime(seconds: 90, preferredTimescale: 600)
player.seek(to: targetTime)
}
playerViewController.contextualActions = [skipIntroAction]
}
func hideContextualActions(playerViewController: AVPlayerViewController) {
playerViewController.contextualActions = []
}
关键点:
import AVFoundation提供AVPlayer和CMTime。showSkipIntroAction在合适片段出现时调用。UIAction(title: "Skip Intro")创建一个跳过片头操作,对应演讲中的 Skip Intro 示例。CMTime(seconds: 90, preferredTimescale: 600)表示要跳转到的播放时间。player.seek(to:)执行跳转。contextualActions = [skipIntroAction]把操作显示在播放器中。hideContextualActions在不需要时调用。contextualActions = []隐藏当前上下文操作。
最少要遵守的 tvOS 播放实践
(08:48)
演讲最后给了三条实践建议。
第一,不要给播放器添加额外手势。它会干扰标准播放 UI,也可能在未来系统版本失效。
第二,补齐缺失的 metadata。标题、图片、描述这类信息可以通过 AVPlayerItem.externalMetadata 提供。系统会用这些信息填充 Title View 和 Info 标签。
第三,考虑启用 Picture in Picture(画中画)。它让用户在全屏内容和画中画之间切换,也能同时播放视频并使用其他内容。
import AVKit
func configurePlaybackExperience(playerViewController: AVPlayerViewController) {
playerViewController.allowsPictureInPicturePlayback = true
playerViewController.transportBarIncludesTitleView = true
}
关键点:
allowsPictureInPicturePlayback = true允许播放器使用画中画能力。transportBarIncludesTitleView = true保持标题区域显示。- 这些设置建立在
AVPlayerViewController的标准播放体验之上。 - 手势、焦点和播放控制继续由系统界面处理。
核心启发
1. 给视频 App 增加“继续观看”推荐标签
- 做什么:在播放器内添加 Recommended 标签,展示用户还没看完的剧集或相关内容。
- 为什么值得做:Content Tabs 可以让用户浏览推荐内容,同时留在播放界面里。
- 怎么开始:创建一个自定义
UIViewController,设置preferredContentSize和title,再放进AVPlayerViewController.customInfoViewControllers。
2. 给课程视频增加章节导航
- 做什么:在播放界面显示课程章节,用户可以直接跳到某个小节。
- 为什么值得做:演讲提到
AVPlayerItem.navigationMarkerGroups会让系统显示 Chapters 标签。 - 怎么开始:为
AVPlayerItem提供章节 marker group,并用AVPlayerViewController播放该 item。
3. 给剧集增加“跳过片头”按钮
- 做什么:片头出现时显示 Skip Intro,片头结束后隐藏。
- 为什么值得做:Contextual Actions 专门用于播放过程中的相关操作,交互行为和系统焦点保持一致。
- 怎么开始:用播放器的 periodic 或 boundary time observer 监听时间,进入片头区间时设置
contextualActions,离开后设为空数组。
4. 给播放控制条增加收藏入口
- 做什么:用户看节目时,可以直接在播放控制条把当前内容加入收藏。
- 为什么值得做:
transportBarCustomMenuItems支持UIAction,适合放置和当前媒体强相关的轻量操作。 - 怎么开始:创建
UIAction(title:image:handler:),把它加入playerViewController.transportBarCustomMenuItems。
5. 给人物访谈添加嘉宾标签页
- 做什么:在播放器里展示本期嘉宾、主持人或角色列表。
- 为什么值得做:
TVMonogramContentConfiguration提供人物头像样式,适合放在 Content Tabs 的 collection view 中。 - 怎么开始:用 collection view 承载人物列表,在 cell 中设置
TVMonogramContentConfiguration.cell()。
关联 Session
- What’s new in AVKit — 继续了解 AVKit 在画中画和全屏体验上的更新。
- Explore dynamic pre-rolls and mid-rolls in HLS — 学习如何用 HLS 和 AVFoundation 处理片前、片中插播内容。
- Coordinate media experiences with Group Activities — 把媒体播放接入 SharePlay,同步多人观看体验。
- Meet Group Activities — 理解 Group Activities 的会话模型和 SharePlay 基础概念。
评论
GitHub Issues · utterances