使用 AVKit 构建媒体播放体验。适用于通过 AVPlayerViewController 添加视频播放器、开启画中画(Picture-in-Picture)、通过 AirPlay 投屏/路由媒体、使用 SwiftUI VideoPlayer 视图、配置播放控制条、显示字幕与隐藏字幕(Closed Captions),或将 AVFoundation 播放功能与系统 UI 进行集成等场景。
AVKit
基于 AVFoundation 构建的高层级媒体播放 UI。提供系统标准视频播放器、画中画(Picture-in-Picture)、AirPlay 投屏路由、播放控制条以及字幕/隐藏字幕显示功能。面向 Swift 6.3 / iOS 26+。
目录
- 准备工作
- AVPlayerViewController
- SwiftUI VideoPlayer
- 画中画 (Picture-in-Picture)
- AirPlay
- 播放控制与播放速度
- 字幕与隐藏字幕
- 常见错误
- 审查清单
- 参考资料
准备工作
音频会话配置
支持后台音频、AirPlay 或画中画(PiP)的播放类 App 需要配置相应的音频会话类别(Audio Session Category)及对应的后台模式(Background Mode)。
- 启用 Background Modes > Audio, AirPlay, and Picture in Picture(即
UIBackgroundModes中的audio值) - 将音频会话类别设置为
.playback - 延迟调用
setActive(true)直至开始播放,避免过早打断其它应用播放的音频
import AVFoundation
func configureAudioSessionForPlayback() {
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.playback, mode: .moviePlayback)
} catch {
print("Audio session category failed: \(error)")
}
}
func activateAudioSessionWhenPlaybackBegins() {
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print("Audio session activation failed: \(error)")
}
}
导入模块
import AVKit // AVPlayerViewController, VideoPlayer, PiP
import AVFoundation // AVPlayer, AVPlayerItem, AVAsset
AVPlayerViewController
AVPlayerViewController 是 UIKit 框架下的标准播放器,开箱即用支持系统原生播放控制条、画中画(PiP)、AirPlay 投屏、字幕显示以及逐帧分析等功能。请勿对其创建子类。
基础展示(全屏)
import AVKit
func presentPlayer(from viewController: UIViewController, url: URL) {
let player = AVPlayer(url: url)
let playerVC = AVPlayerViewController()
playerVC.player = player
viewController.present(playerVC, animated: true) {
player.play()
}
}
内联(嵌入式)播放
如需在页面内部内联播放视频,需将 AVPlayerViewController 作为子视图控制器(Child View Controller)添加。依次调用 addChild、使用布局约束添加视图,最后调用 didMove(toParent:)。
func embedPlayer(in parent: UIViewController, container: UIView, url: URL) {
let playerVC = AVPlayerViewController()
playerVC.player = AVPlayer(url: url)
parent.addChild(playerVC)
container.addSubview(playerVC.view)
playerVC.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
playerVC.view.leadingAnchor.constraint(equalTo: container.leadingAnchor),
playerVC.view.trailingAnchor.constraint(equalTo: container.trailingAnchor),
playerVC.view.topAnchor.constraint(equalTo: container.topAnchor),
playerVC.view.bottomAnchor.constraint(equalTo: container.bottomAnchor)
])
playerVC.didMove(toParent: parent)
}
关键属性
playerVC.showsPlaybackControls = true // 显示/隐藏系统播放控制条
playerVC.videoGravity = .resizeAspect // 使用 .resizeAspectFill 可进行裁剪填充
playerVC.entersFullScreenWhenPlaybackBegins = false
playerVC.exitsFullScreenWhenPlaybackEnds = true
playerVC.updatesNowPlayingInfoCenter = true // 自动更新 MPNowPlayingInfoCenter
使用 contentOverlayView 可以在视频画面与播放控制条之间添加非交互式视图(如水印、Logo 等)。
Delegate 代理回调
遵循 AVPlayerViewControllerDelegate 协议可以响应全屏切换、画中画生命周期事件、插播广告播放以及媒体选择变更等。利用转场协调器的 animate(alongsideTransition:completion:) 方法,能够使自定义 UI 与全屏动画保持同步。
准备就绪状态检测
在展示播放器之前观察 isReadyForDisplay 属性,以避免画面出现黑屏闪烁:
let observation = playerVC.observe(\.isReadyForDisplay) { observed, _ in
if observed.isReadyForDisplay {
// 安全展示播放器视图
}
}
SwiftUI VideoPlayer
SwiftUI 中的 VideoPlayer 视图封装了 AVKit 的播放 UI。
基础用法
import SwiftUI
import AVKit
struct PlayerView: View {
@State private var player: AVPlayer?
var body: some View {
Group {
if let player {
VideoPlayer(player: player)
.frame(height: 300)
} else {
ProgressView()
}
}
.task {
let url = URL(string: "https://example.com/video.m3u8")!
player = AVPlayer(url: url)
}
}
}
视频覆盖层
在视频内容上方、系统播放控制条下方添加 SwiftUI 覆盖层(Overlay)。该覆盖层支持交互,但仅会接收系统控制条未处理的事件。
VideoPlayer(player: player) {
VStack {
Spacer()
HStack {
Image("logo")
.resizable()
.frame(width: 40, height: 40)
.padding()
Spacer()
}
}
}
高级控制:嵌入 UIKit
VideoPlayer 并未暴露 AVPlayerViewController 的所有属性。如需配置画中画(PiP)、代理回调或播放速度控制,可将 AVPlayerViewController 包装在 UIViewControllerRepresentable 中。详见 references/avkit-patterns.md 中的完整模式说明。
画中画 (Picture-in-Picture)
画中画(PiP)允许用户在使用其它 App 的同时,通过浮窗观看视频。只要 App 完成相应配置、设备支持 PiP,且当前 AVPlayerItem 包含符合 AVPlayer 格式的可播放视频内容,AVPlayerViewController 就会自动支持画中画。哪怕 App 和设备配置均无误,纯音频资源、不支持的容器/编解码格式、或尚未准备好展示视频的资源都会导致 PiP 无法启动。对于自定义播放器 UI,请直接使用 AVPictureInPictureController。
前提条件
- 音频会话类别设置为
.playback(参见准备工作) - 已启用 Background Modes > Audio, AirPlay, and Picture in Picture
- 处于就绪状态且包含可播放视频媒体(而非纯音频内容)的
AVPlayerItem - 当前播放上下文允许使用 PiP;自定义播放器需观察
isPictureInPicturePossible属性
标准播放器画中画
AVPlayerViewController 默认开启 PiP 支持。可通过以下属性控制自动激活以及内联转画中画过渡:
let playerVC = AVPlayerViewController()
playerVC.player = player
// PiP 默认启用;设为 false 可禁用
playerVC.allowsPictureInPicturePlayback = true
// 当 App 切到后台时自动开启 PiP(适用于内联/非全屏播放器)
playerVC.canStartPictureInPictureAutomaticallyFromInline = true
画中画停止时恢复 UI
当用户点击画中画浮窗中的恢复按钮时,需实现代理方法重新展示播放器。调用完成回调(completion handler)并传入 true,以通知系统完成 UI 恢复动画。
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
// 重新展示或重新嵌入播放器视图控制器
present(playerViewController, animated: false) {
completionHandler(true)
}
}
自定义播放器画中画
针对自定义播放器 UI,需配合 AVPlayerLayer 或 Sample Buffer 内容源使用 AVPictureInPictureController。在创建画中画 UI 之前先检查设备支持情况,而后在当前播放上下文中启动 PiP 前检查控制器的 isPictureInPicturePossible。完整自定义播放器及 Sample Buffer 画中画模式请参阅 references/avkit-patterns.md。
guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
let pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = true
// 应由用户的画中画按钮点击事件触发,切勿自动调用
if pipController.isPictureInPicturePossible {
pipController.startPictureInPicture()
}
广告期间的线性播放
插播广告(Interstitial breaks)可以来自媒体流/Manifest 说明文件(AVFoundation 会通过 AVPlayerItem.interstitialTimeRanges 暴露),也可以来自 App 自定义的 AVPlayerInterstitialEventController 调度计划。在 iOS 上切勿直接为 interstitialTimeRanges 赋值。仅在必须禁止快进/快退的广告或法律声明片段中将 requiresLinearPlayback 设为 true:
// 播放广告期间
playerVC.requiresLinearPlayback = true
// 广告播放结束后
playerVC.requiresLinearPlayback = false
AirPlay
只要 App 配置、媒体资源、网络路由和设备支持允许外部播放,AVPlayerViewController 就会自动开启对 AirPlay 的支持。使用标准播放器时无需编写额外代码。当附近检测到支持 AirPlay 的设备时,系统会在播放控制条中自动显示 AirPlay 按钮。
AVRoutePickerView
在播放器 UI 之外添加独立的 AirPlay 路由选择按钮:
import AVKit
func addRoutePicker(to containerView: UIView) {
let routePicker = AVRoutePickerView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
routePicker.activeTintColor = .systemBlue
routePicker.prioritizesVideoDevices = true // 优先展示支持视频投屏的设备
containerView.addSubview(routePicker)
}
外部播放
AVPlayer 默认允许外部播放。请保持其开启状态以保证 AirPlay 正常工作;如果项目其它位置的代码可能关闭它,则需显式设置:
player.allowsExternalPlayback = true
仅当希望播放器在外部屏幕模式激活时自动切换到外部播放,才需要设置 usesExternalPlaybackWhileExternalScreenIsActive。
播放控制与播放速度
自定义播放速度
在播放器 UI 中提供可供用户选择的倍速选项:
let playerVC = AVPlayerViewController()
playerVC.speeds = [
AVPlaybackSpeed(rate: 0.5, localizedName: "0.5 倍速"),
AVPlaybackSpeed(rate: 1.0, localizedName: "正常"),
AVPlaybackSpeed(rate: 1.5, localizedName: "1.5 倍速"),
AVPlaybackSpeed(rate: 2.0, localizedName: "2.0 倍速")
]
使用 AVPlaybackSpeed.systemDefaultSpeeds 可恢复系统默认的倍速选项。
快进/快退与进度拖拽
在 iOS 上,请使用标准的播放控制条,自定义控件则使用 AVPlayer.seek(...)。AVPlayerViewController 的快进快退行为 API(如 isSkipForwardEnabled、isSkipBackwardEnabled 和 skippingBehavior)主要面向 tvOS,iOS 播放器实现中应避免使用。
正在播放 (Now Playing) 信息集成
AVPlayerViewController 默认会自动更新 MPNowPlayingInfoCenter。如果您需要手动管理“正在播放”信息,可将其禁用:
playerVC.updatesNowPlayingInfoCenter = false
字幕与隐藏字幕
当媒体包含相应的文本轨道时,AVKit 会自动处理字幕及隐藏字幕(Closed Captions)的显示。用户可以在“设置 > 辅助功能 > 字幕与隐藏字幕”中控制字幕偏好设置。
代码控制字幕选择
let asset = player.currentItem?.asset
if let group = try await asset?.loadMediaSelectionGroup(for: .legible),
let english = group.options.first(where: { option in
option.locale?.language.languageCode?.identifier == "en"
}) {
player.currentItem?.se
<!-- truncated for translation batch; full body continues in source -->




