使用 AVKit 打造媒體播放體驗。適用於以下情境:使用 AVPlayerViewController 新增影片播放器、啟用子母畫面 (Picture-in-Picture)、透過 AirPlay 串流媒體、使用 SwiftUI 的 VideoPlayer 視圖、設定播放控制元件、顯示字幕與 CC 字幕 (Closed Captions),或將 AVFoundation 播放功能整合至系統 UI。
AVKit
建置於 AVFoundation 之上的高階媒體播放 UI。提供系統標準的影片播放器、子母畫面、AirPlay 串流、播放控制元件以及字幕/CC 字幕顯示。適用於 Swift 6.3 / iOS 26+。
目錄
環境設定
音訊會話設定
當播放類 App 需要支援背景音訊、AirPlay 或子母畫面 (PiP) 時,必須設定對應的音訊會話類別 (Audio Session Category) 與相符的背景模式。
- 啟用 Background Modes > Audio, AirPlay, and Picture in Picture(即
UIBackgroundModes中的audio值) - 將音訊會話類別設定為
.playback - 延後呼叫
setActive(true)直到播放正式開始,避免過早中斷其他 App 的音訊
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、字幕以及畫面分析功能。請勿將其繼承子類化 (Subclass)。
基本呈現方式(全螢幕)
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()
}
}
內嵌播放 (Inline Playback)
將 AVPlayerViewController 作為子視圖控制器 (Child View Controller) 加入以實現內嵌播放。
請呼叫 addChild,設定 Constraint 加入視圖,最後呼叫 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 以回應全螢幕轉場、子母畫面 (PiP) 生命週期事件、插播廣告播放以及媒體選擇變更。
可使用轉場協調器 (Transition Coordinator) 的 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 Hosting 取得進階控制權
VideoPlayer 並未公開 AVPlayerViewController 的所有屬性。若需要配置 PiP、處理 Delegate 回呼或控制播放速度,請將 AVPlayerViewController 封裝在 UIViewControllerRepresentable 中。完整模式請參閱 references/avkit-patterns.md。
子母畫面
子母畫面 (PiP) 讓使用者能在使用其他 App 時,透過浮動視窗觀看影片。
當 App 完成相關配置、裝置支援 PiP,且目前的 AVPlayerItem 是相容於 AVPlayer 的可播放影片內容時,AVPlayerViewController 會自動支援 PiP。即使 App 與裝置設定正確,僅含音訊的項目、不支援的容器/編解碼器,或是尚未準備好顯示影片的項目,仍可能導致 PiP 無法使用。若為自訂播放器 UI,請直接使用 AVPictureInPictureController。
先決條件
- 音訊會話類別設定為
.playback(參閱環境設定) - 啟用 Background Modes > Audio, AirPlay, and Picture in Picture
- 準備好含有可播放影片媒體的
AVPlayerItem(而非僅音訊內容) - 目前播放情境允許 PiP;若為自訂播放器,請監聽
isPictureInPicturePossible
標準播放器 PiP
AVPlayerViewController 預設啟用 PiP。控制自動啟用以及從內嵌切換至 PiP 的轉場行為:
let playerVC = AVPlayerViewController()
playerVC.player = player
// 預設啟用 PiP;設定為 false 以停用
playerVC.allowsPictureInPicturePlayback = true
// 當 App 進入背景時自動啟動 PiP(適用於內嵌/非全螢幕播放器)
playerVC.canStartPictureInPictureAutomaticallyFromInline = true
當 PiP 停止時還原 UI
當使用者點擊 PiP 視窗中的還原按鈕時,請實作 Delegate 方法以重新呈現播放器。呼叫 completion handler 並傳入 true,以通知系統完成還原動畫。
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
// 重新呈現或重新內嵌播放器視圖控制器
present(playerViewController, animated: false) {
completionHandler(true)
}
}
自訂播放器 PiP
對於自訂播放器 UI,請搭配 AVPlayerLayer 或 Sample Buffer 內容來源使用 AVPictureInPictureController。建立 PiP UI 之前請先檢查裝置支援度,並在目前的播放情境中啟動 PiP 之前檢查控制器之 isPictureInPicturePossible 屬性。請參閱 references/avkit-patterns.md 以獲取完整的自訂播放器與 Sample Buffer PiP 模式。
guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
let pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = true
// 請由使用者的 PiP 按鈕動作觸發此方法,絕不要自動呼叫。
if pipController.isPictureInPicturePossible {
pipController.startPictureInPicture()
}
廣告期間的線性播放
插播廣告時段可來自媒體串流/Manifest(AVFoundation 透過 AVPlayerItem.interstitialTimeRanges 公開),或是來自 App 自行的 AVPlayerInterstitialEventController 排程。在 iOS 上請勿直接指定 interstitialTimeRanges。請僅在必須防止使用者在必要的廣告或法律聲明片段中進行尋軌 (Seek) 時使用 requiresLinearPlayback:
// 播放廣告期間
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: "Half Speed"),
AVPlaybackSpeed(rate: 1.0, localizedName: "Normal"),
AVPlaybackSpeed(rate: 1.5, localizedName: "1.5x"),
AVPlaybackSpeed(rate: 2.0, localizedName: "Double Speed")
]
使用 AVPlaybackSpeed.systemDefaultSpeeds 可還原預設的播放速度選項。
快轉/倒帶與尋軌
在 iOS 上,自訂 App 控制元件請使用標準播放控制介面與 AVPlayer.seek(...)。AVPlayerViewController 的快轉/倒帶行為 API(例如 isSkipForwardEnabled、isSkipBackwardEnabled 與 skippingBehavior)主要針對 tvOS 設計;請勿在 iOS 播放器實作中使用這些 API。
即時播放資訊整合
AVPlayerViewController 預設會自動更新 MPNowPlayingInfoCenter。如果您需要手動管理即時播放資訊 (Now Playing Info),請停用此功能:
playerVC.updatesNowPlayingInfoCenter = false
字幕與 CC 字幕
當媒體包含適當的文字軌時,AVKit 會自動處理字幕與 CC 字幕 (Closed Caption) 的顯示。使用者可在「設定」>「輔助使用」>「字幕與隱藏字幕」中控制字幕偏好設定。
程式化選擇字幕
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




