avkit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAVKit
AVKit
High-level media playback UI built on AVFoundation. Provides system-standard
video players, Picture-in-Picture, AirPlay routing, transport controls, and
subtitle/caption display. Targets Swift 6.3 / iOS 26+.
基于AVFoundation构建的高级媒体播放UI。提供系统标准的视频播放器、画中画(Picture-in-Picture)、AirPlay媒体路由、播放控制以及字幕/隐藏式字幕显示功能。适配Swift 6.3 / iOS 26+。
Contents
配置步骤
—
音频会话配置
在进行任何播放操作前,先配置音频会话和后台模式。如果跳过这一步,画中画和后台音频会静默失效。
- 在Info.plist的中添加
UIBackgroundModes后台模式audio - 将音频会话类别设置为
.playback
swift
import AVFoundation
func configureAudioSession() {
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.playback, mode: .moviePlayback)
try session.setActive(true)
} catch {
print("Audio session configuration failed: \(error)")
}
}Setup
导入依赖
Audio Session Configuration
—
Configure the audio session and background modes before any playback. Without
this, PiP and background audio fail silently.
- Add the background mode to
audioin Info.plistUIBackgroundModes - Set the audio session category to
.playback
swift
import AVFoundation
func configureAudioSession() {
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.playback, mode: .moviePlayback)
try session.setActive(true)
} catch {
print("Audio session configuration failed: \(error)")
}
}swift
import AVKit // AVPlayerViewController, VideoPlayer, PiP
import AVFoundation // AVPlayer, AVPlayerItem, AVAssetImports
AVPlayerViewController
swift
import AVKit // AVPlayerViewController, VideoPlayer, PiP
import AVFoundation // AVPlayer, AVPlayerItem, AVAssetAVPlayerViewControllerAVPlayerViewController
基础全屏展示
AVPlayerViewControllerswift
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()
}
}Basic Presentation (Full Screen)
内嵌式播放
swift
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()
}
}将作为子视图控制器添加以实现内嵌播放。调用,添加视图并设置约束,然后调用。
AVPlayerViewControlleraddChilddidMove(toParent:)swift
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)
}Inline (Embedded) Playback
关键属性
Add as a child view controller for inline playback.
Call , add the view with constraints, then call .
AVPlayerViewControlleraddChilddidMove(toParent:)swift
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)
}swift
playerVC.showsPlaybackControls = true // Show/hide system controls
playerVC.videoGravity = .resizeAspect // .resizeAspectFill to crop
playerVC.entersFullScreenWhenPlaybackBegins = false
playerVC.exitsFullScreenWhenPlaybackEnds = true
playerVC.updatesNowPlayingInfoCenter = true // Auto-updates MPNowPlayingInfoCenter使用在视频和播放控制栏之间添加非交互视图(如水印、logo)。
contentOverlayViewKey Properties
代理
swift
playerVC.showsPlaybackControls = true // Show/hide system controls
playerVC.videoGravity = .resizeAspect // .resizeAspectFill to crop
playerVC.entersFullScreenWhenPlaybackBegins = false
playerVC.exitsFullScreenWhenPlaybackEnds = true
playerVC.updatesNowPlayingInfoCenter = true // Auto-updates MPNowPlayingInfoCenterUse to add non-interactive views (watermarks, logos)
between the video and transport controls.
contentOverlayView遵循协议以响应全屏切换、画中画生命周期事件、插播内容播放以及媒体选择变更。使用过渡协调器的方法将你的UI与全屏动画同步。
AVPlayerViewControllerDelegateanimate(alongsideTransition:completion:)Delegate
显示就绪状态
Adopt to respond to full-screen transitions,
PiP lifecycle events, interstitial playback, and media selection changes.
Use the transition coordinator's to
synchronize your UI with full-screen animations.
AVPlayerViewControllerDelegateanimate(alongsideTransition:completion:)在展示播放器前监听属性,避免出现黑屏闪烁:
isReadyForDisplayswift
let observation = playerVC.observe(\.isReadyForDisplay) { observed, _ in
if observed.isReadyForDisplay {
// Safe to show the player view
}
}Display Readiness
SwiftUI VideoPlayer
Observe before showing the player to avoid a black flash:
isReadyForDisplayswift
let observation = playerVC.observe(\.isReadyForDisplay) { observed, _ in
if observed.isReadyForDisplay {
// Safe to show the player view
}
}VideoPlayerSwiftUI VideoPlayer
基础用法
The SwiftUI view wraps AVKit's playback UI.
VideoPlayerswift
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)
}
}
}Basic Usage
视频叠加层
swift
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叠加层。
swift
VideoPlayer(player: player) {
VStack {
Spacer()
HStack {
Image("logo")
.resizable()
.frame(width: 40, height: 40)
.padding()
Spacer()
}
}
}Video Overlay
借助UIKit实现高级控制
Add a non-interactive SwiftUI overlay on top of video content.
swift
VideoPlayer(player: player) {
VStack {
Spacer()
HStack {
Image("logo")
.resizable()
.frame(width: 40, height: 40)
.padding()
Spacer()
}
}
}VideoPlayerAVPlayerViewControllerAVPlayerViewControllerUIViewControllerRepresentableUIKit Hosting for Advanced Control
画中画(Picture-in-Picture)
VideoPlayerAVPlayerViewControllerAVPlayerViewControllerUIViewControllerRepresentable画中画功能允许用户在使用其他应用时,以浮动窗口形式观看视频。一旦配置好音频会话,会自动支持画中画。对于自定义播放器UI,请直接使用。
AVPlayerViewControllerAVPictureInPictureControllerPicture-in-Picture
前提条件
PiP lets users watch video in a floating window while using other apps.
supports PiP automatically once the audio session is
configured. For custom player UIs, use directly.
AVPlayerViewControllerAVPictureInPictureController- 音频会话类别设置为(参见配置步骤)
.playback - 在中添加
UIBackgroundModes后台模式audio
Prerequisites
标准播放器画中画
- Audio session category set to (see Setup)
.playback - background mode in
audioUIBackgroundModes
AVPlayerViewControllerswift
let playerVC = AVPlayerViewController()
playerVC.player = player
// 画中画默认启用;设置为false可禁用
playerVC.allowsPictureInPicturePlayback = true
// 当应用进入后台时自动启动画中画(适用于内嵌/非全屏播放器)
playerVC.canStartPictureInPictureAutomaticallyFromInline = trueStandard Player PiP
画中画停止时恢复UI
PiP is enabled by default on . Control automatic
activation and inline-to-PiP transitions:
AVPlayerViewControllerswift
let playerVC = AVPlayerViewController()
playerVC.player = player
// PiP enabled by default; set false to disable
playerVC.allowsPictureInPicturePlayback = true
// Auto-start PiP when app backgrounds (for inline/non-fullscreen players)
playerVC.canStartPictureInPictureAutomaticallyFromInline = true当用户点击画中画窗口的恢复按钮时,实现代理方法以重新展示你的播放器。调用完成处理程序并传入,通知系统完成恢复动画。
trueswift
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
// Re-present or re-embed the player view controller
present(playerViewController, animated: false) {
completionHandler(true)
}
}Restoring the UI When PiP Stops
自定义播放器画中画
When the user taps the restore button in PiP, implement the delegate method to
re-present your player. Call the completion handler with to signal the
system to finish the restore animation.
trueswift
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
// Re-present or re-embed the player view controller
present(playerViewController, animated: false) {
completionHandler(true)
}
}对于自定义播放器UI,请结合或样本缓冲区内容源使用。首先检查是否支持。完整的自定义播放器和样本缓冲区画中画模式请参考references/avkit-patterns.md。
AVPlayerLayerAVPictureInPictureControllerisPictureInPictureSupported()swift
guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
let pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = trueCustom Player PiP
广告期间的线性播放
For custom player UIs, use with an
or sample buffer content source. Check first.
See references/avkit-patterns.md for full custom player and sample buffer
PiP patterns.
AVPictureInPictureControllerAVPlayerLayerisPictureInPictureSupported()swift
guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
let pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = true通过切换属性,防止在广告或法律声明播放期间进行快进/后退操作:
requiresLinearPlaybackswift
// 广告播放期间
playerVC.requiresLinearPlayback = true
// 广告播放完成后
playerVC.requiresLinearPlayback = falseLinear Playback During Ads
AirPlay
Prevent seeking during ads or legal notices by toggling :
requiresLinearPlaybackswift
// During an ad
playerVC.requiresLinearPlayback = true
// After the ad completes
playerVC.requiresLinearPlayback = falseAVPlayerViewControllerAirPlay
AVRoutePickerView
AVPlayerViewController在播放器UI外添加独立的AirPlay路由选择按钮:
swift
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 // Show video-capable routes first
containerView.addSubview(routePicker)
}AVRoutePickerView
启用外部播放
Add a standalone AirPlay route picker button outside the player UI:
swift
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 // Show video-capable routes first
containerView.addSubview(routePicker)
}确保允许外部播放(默认已启用):
AVPlayerswift
player.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = trueEnabling External Playback
播放控制与播放速度
—
自定义播放速度
Ensure allows external playback (enabled by default):
AVPlayerswift
player.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = true在播放器UI中提供用户可选的播放速度:
swift
let playerVC = AVPlayerViewController()
playerVC.speeds = [
AVPlaybackSpeed(rate: 0.5, localizedName: "Half Speed"),
AVPlaybackSpeed(rate: 1.0, localizedName: "Normal"),
AVPlaybackSpeed(rate: 1.5, localizedName: "1.5x"),
AVPlaybackSpeed(rate: 2.0, localizedName: "Double Speed")
]使用恢复默认速度选项。
AVPlaybackSpeed.systemDefaultSpeedsTransport Controls and Playback Speed
跳过功能
Custom Playback Speeds
—
Provide user-selectable playback speeds in the player UI:
swift
let playerVC = AVPlayerViewController()
playerVC.speeds = [
AVPlaybackSpeed(rate: 0.5, localizedName: "Half Speed"),
AVPlaybackSpeed(rate: 1.0, localizedName: "Normal"),
AVPlaybackSpeed(rate: 1.5, localizedName: "1.5x"),
AVPlaybackSpeed(rate: 2.0, localizedName: "Double Speed")
]Use to restore the default speed options.
AVPlaybackSpeed.systemDefaultSpeeds配置快进/后退跳过控制:
swift
playerVC.isSkipForwardEnabled = true
playerVC.isSkipBackwardEnabled = trueSkipping Behavior
现在播放信息集成
Configure forward/backward skip controls:
swift
playerVC.isSkipForwardEnabled = true
playerVC.isSkipBackwardEnabled = trueAVPlayerViewControllerMPNowPlayingInfoCenterswift
playerVC.updatesNowPlayingInfoCenter = falseNow Playing Integration
字幕与隐藏式字幕
AVPlayerViewControllerMPNowPlayingInfoCenterswift
playerVC.updatesNowPlayingInfoCenter = false当媒体包含合适的文本轨道时,AVKit会自动处理字幕和隐藏式字幕的显示。用户可在“设置 > 辅助功能 > 字幕与隐藏式字幕”中控制字幕偏好。
Subtitles and Closed Captions
限制字幕语言
AVKit handles subtitle and closed caption display automatically when the media
contains appropriate text tracks. Users control subtitle preferences in
Settings > Accessibility > Subtitles & Captioning.
swift
// 仅显示英文和西班牙文字幕选项
playerVC.allowedSubtitleOptionLanguages = ["en", "es"]Restricting Subtitle Languages
强制显示字幕
swift
// Only show English and Spanish subtitle options
playerVC.allowedSubtitleOptionLanguages = ["en", "es"]强制始终显示字幕(用户无法关闭):
swift
playerVC.requiresFullSubtitles = trueRequiring Subtitles
媒体选择(代理)
Force subtitles to always display (the user cannot turn them off):
swift
playerVC.requiresFullSubtitles = true响应用户切换字幕或音轨的操作:
swift
func playerViewController(
_ playerViewController: AVPlayerViewController,
didSelect mediaSelectionOption: AVMediaSelectionOption?,
in mediaSelectionGroup: AVMediaSelectionGroup
) {
if let option = mediaSelectionOption {
print("Selected: \(option.displayName)")
}
}Media Selection (Delegate)
在HLS中提供字幕轨道
Respond to the user changing subtitle or audio track selection:
swift
func playerViewController(
_ playerViewController: AVPlayerViewController,
didSelect mediaSelectionOption: AVMediaSelectionOption?,
in mediaSelectionGroup: AVMediaSelectionGroup
) {
if let option = mediaSelectionOption {
print("Selected: \(option.displayName)")
}
}字幕和隐藏式字幕嵌入在HLS清单中。AVKit会从的中读取这些轨道。对于本地文件,请使用添加轨道。
AVAssetAVMediaSelectionGroupAVMutableCompositionAVMediaCharacteristic.legibleProviding Subtitle Tracks in HLS
常见错误
—
不要:子类化AVPlayerViewController
Subtitles and closed captions are embedded in HLS manifests. AVKit reads them
from on the . For local files, use
to add tracks.
AVMediaSelectionGroupAVAssetAVMutableCompositionAVMediaCharacteristic.legibleApple明确声明此操作不受支持。可能会导致未定义行为,或在未来OS版本中崩溃。
swift
// 错误示例
class MyPlayerVC: AVPlayerViewController { } // 不被支持
// 正确做法:使用组合模式+代理
let playerVC = AVPlayerViewController()
playerVC.delegate = coordinatorCommon Mistakes
不要:跳过画中画的音频会话配置
DON'T: Subclass AVPlayerViewController
—
Apple explicitly states this is unsupported. It may cause undefined behavior or
crash on future OS versions.
swift
// WRONG
class MyPlayerVC: AVPlayerViewController { } // Unsupported
// CORRECT: Use composition with delegation
let playerVC = AVPlayerViewController()
playerVC.delegate = coordinator如果音频会话未设置为,画中画会静默失效。后台音频也会停止工作。
.playbackswift
// 错误示例:使用默认音频会话
let playerVC = AVPlayerViewController()
playerVC.player = player // 画中画无法工作
// 正确做法:先配置音频会话
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
try AVAudioSession.sharedInstance().setActive(true)
let playerVC = AVPlayerViewController()
playerVC.player = playerDON'T: Skip audio session configuration for PiP
不要:忘记实现画中画恢复代理或其完成处理程序
PiP fails silently if the audio session is not set to . Background
audio also stops working.
.playbackswift
// WRONG: Default audio session
let playerVC = AVPlayerViewController()
playerVC.player = player // PiP won't work
// CORRECT: Configure audio session first
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
try AVAudioSession.sharedInstance().setActive(true)
let playerVC = AVPlayerViewController()
playerVC.player = player如果没有实现,系统无法将用户带回你的播放器。如果不调用,会导致系统状态不一致。
restoreUserInterfaceForPictureInPictureStopWithCompletionHandlercompletionHandler(true)swift
// 错误示例:没有实现代理方法或未调用完成处理程序
// 用户点击画中画的恢复按钮 -> 无反应或动画卡住
// 正确做法
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
present(playerViewController, animated: false) {
completionHandler(true)
}
}DON'T: Forget the PiP restore delegate or its completion handler
不要:在SwiftUI视图的init中创建AVPlayer
Without , the
system cannot return the user to your player. Failing to call
leaves the system in an inconsistent state.
restoreUserInterfaceForPictureInPictureStopWithCompletionHandlercompletionHandler(true)swift
// WRONG: No delegate method or missing completionHandler call
// User taps restore in PiP -> nothing happens or animation hangs
// CORRECT
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
present(playerViewController, animated: false) {
completionHandler(true)
}
}提前创建播放器会导致性能问题。SwiftUI可能会多次重建视图。
swift
// 错误示例:每次视图初始化时创建
struct PlayerView: View {
let player = AVPlayer(url: videoURL) // 每次视图评估时都会重建
var body: some View { VideoPlayer(player: player) }
}
// 正确做法:使用@State并延迟创建
struct PlayerView: View {
@State private var player: AVPlayer?
var body: some View {
VideoPlayer(player: player)
.task { player = AVPlayer(url: videoURL) }
}
}DON'T: Create AVPlayer in a SwiftUI view's init
审核清单
Creating the player eagerly causes performance issues. SwiftUI may recreate the
view multiple times.
swift
// WRONG: Created on every view init
struct PlayerView: View {
let player = AVPlayer(url: videoURL) // Re-created on every view evaluation
var body: some View { VideoPlayer(player: player) }
}
// CORRECT: Use @State and defer creation
struct PlayerView: View {
@State private var player: AVPlayer?
var body: some View {
VideoPlayer(player: player)
.task { player = AVPlayer(url: videoURL) }
}
}- 音频会话类别已设置为并使用
.playbackmode: .moviePlayback - 中已添加
UIBackgroundModes后台模式audio - 未对进行子类化
AVPlayerViewController - 已实现画中画恢复代理方法并调用了
completionHandler(true) - 在SwiftUI中使用延迟创建
.task(而非提前创建)AVPlayer - 对内嵌播放器设置了
canStartPictureInPictureAutomaticallyFromInline - 仅在必要的广告/法律片段期间切换
requiresLinearPlayback - 已启用
AVPlayer以支持AirPlayallowsExternalPlayback - 已使用实际媒体轨道测试字幕语言限制
- 已正确设置视频缩放模式(或
.resizeAspect).resizeAspectFill - 在展示播放器视图前监听了
isReadyForDisplay - 已处理网络流内容的错误(HLS加载失败、超时等)
Review Checklist
参考资料
- Audio session category set to with
.playbackmode: .moviePlayback - background mode added to
audioin Info.plistUIBackgroundModes - is not subclassed
AVPlayerViewController - PiP restore delegate method implemented and calls
completionHandler(true) - deferred to
AVPlayerin SwiftUI (not created eagerly).task - set for inline players
canStartPictureInPictureAutomaticallyFromInline - toggled only during required ad/legal segments
requiresLinearPlayback - enabled on
allowsExternalPlaybackfor AirPlay supportAVPlayer - Subtitle language restrictions tested with actual media tracks
- Video gravity set appropriately (vs
.resizeAspect).resizeAspectFill - observed before showing the player view
isReadyForDisplay - Error handling for network-streamed content (HLS failures, timeouts)
- 高级模式(自定义播放器UI、插播内容、后台播放、错误处理):references/avkit-patterns.md
- AVKit框架
- AVPlayerViewController
- VideoPlayer (SwiftUI)
- AVPictureInPictureController
- AVRoutePickerView
- AVPlaybackSpeed
- 为媒体播放配置你的应用
- 在标准播放器中采用画中画
- 在标准用户界面中播放视频内容
References
—
- Advanced patterns (custom player UI, interstitials, background playback, error handling): references/avkit-patterns.md
- AVKit framework
- AVPlayerViewController
- VideoPlayer (SwiftUI)
- AVPictureInPictureController
- AVRoutePickerView
- AVPlaybackSpeed
- Configuring your app for media playback
- Adopting Picture in Picture in a Standard Player
- Playing video content in a standard user interface
—