Communitygithub.com

JordanCoin/ios-skills-collection

使用 AVKit 建立媒體播放體驗。適用於使用 AVPlayerViewController 加入影片播放器、啟用子母畫面、透過 AirPlay 路由媒體、使用 SwiftUI VideoPlayer 視圖、設定傳輸控制項、顯示字幕與隱藏式字幕,或將 AVFoundation 播放與系統 UI 整合。

ios-skills-collection 是什麼?

這個技能提供使用 AVKit 框架建立高品質媒體播放體驗的完整指南。它涵蓋了 AVPlayerViewController 的 UIKit 與 SwiftUI VideoPlayer 兩種實作方式,詳細說明子母畫面(PiP)、AirPlay 路由、傳輸控制項自訂、字幕與隱藏式字幕顯示等關鍵功能。開發者可以透過清晰的程式碼範例學習如何在 iOS 26+ 與 Swift 6.3 環境中正確設定音訊會話、嵌入播放器、處理 PiP 還原委派,以及避免常見的錯誤模式。此技能支援所有 Apple 平台,特別針對 iPadOS 與 iOS 最佳化,也涵蓋了 SwiftUI 與 UIKit 的互通整合。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/JordanCoin/ios-skills-collection/tree/HEAD/skills/dpearson2699--avkit

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

AVKit

以 AVFoundation 為基礎的高層級媒體播放 UI。提供系統標準的影片播放器、子母畫面、AirPlay 路由、傳輸控制項及字幕/隱藏式字幕顯示。目標為 Swift 6.3 / iOS 26+。

目錄

設定

音訊會話設定

在任何播放操作之前,請先設定音訊會話與背景模式。否則,PiP 與背景音訊會默默失敗。

  1. 在 Info.plist 的 UIBackgroundModes 中新增 audio 背景模式
  2. 將音訊會話類別設定為 .playback
import AVFoundation

func configureAudioSession() {
    let session = AVAudioSession.sharedInstance()
    do {
        try session.setCategory(.playback, mode: .moviePlayback)
        try session.setActive(true)
    } catch {
        print("音訊會話設定失敗:\(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 作為子視圖控制器加入以進行內嵌播放。依序呼叫 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 在影片與傳輸控制項之間加入非互動式視圖(例如浮水印、標誌)。

委派

採用 AVPlayerViewControllerDelegate 以回應全螢幕轉換、PiP 生命週期事件、插播播放及媒體選擇變更。使用轉場協調器的 animate(alongsideTransition:completion:) 讓你的 UI 與全螢幕動畫同步。

顯示就緒狀態

在顯示播放器之前觀察 isReadyForDisplay,以避免黑色閃爍:

let observation = playerVC.observe(\.isReadyForDisplay) { observed, _ in
    if observed.isReadyForDisplay {
        // 安全顯示播放器視圖
    }
}

SwiftUI VideoPlayer

VideoPlayer 這個 SwiftUI 視圖封裝了 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 疊加層。

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。

子母畫面

PiP 讓使用者能在使用其他應用程式時,於浮動視窗中觀看影片。音訊會話設定完成後,AVPlayerViewController 會自動支援 PiP。對於自訂播放器 UI,請直接使用 AVPictureInPictureController。

先決條件

  1. 音訊會話類別設為 .playback(請參閱設定)
  2. 在 UIBackgroundModes 中啟用 audio 背景模式

標準播放器的 PiP

AVPlayerViewController 預設啟用 PiP。可控制自動啟動與內嵌到 PiP 的轉換:

let playerVC = AVPlayerViewController()
playerVC.player = player

// PiP 預設啟用;設定為 false 可停用
playerVC.allowsPictureInPicturePlayback = true

// 當應用程式進入背景時自動啟動 PiP(適用於內嵌/非全螢幕播放器)
playerVC.canStartPictureInPictureAutomaticallyFromInline = true

在 PiP 停止時還原 UI

當使用者在 PiP 中點擊還原按鈕時,請實作委派方法以重新呈現你的播放器。使用 true 呼叫完成處理常式,通知系統完成還原動畫。

func playerViewController(
    _ playerViewController: AVPlayerViewController,
    restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
    // 重新呈現或重新嵌入播放器視圖控制器
    present(playerViewController, animated: false) {
        completionHandler(true)
    }
}

自訂播放器的 PiP

對於自訂播放器 UI,請使用 AVPictureInPictureController 搭配 AVPlayerLayer 或取樣緩衝區內容來源。請先檢查 isPictureInPictureSupported()。完整的自訂播放器與取樣緩衝區 PiP 模式請參閱 references/avkit-patterns.md。

guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
let pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = true

廣告期間的線性播放

在廣告或法律聲明期間切換 requiresLinearPlayback 以防止使用者進行搜尋:

// 廣告期間
playerVC.requiresLinearPlayback = true

// 廣告結束後
playerVC.requiresLinearPlayback = false

AirPlay

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 允許外部播放(預設為啟用):

player.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = true

傳輸控制項與播放速度

自訂播放速度

在播放器 UI 中提供使用者可選擇的播放速度:

let playerVC = AVPlayerViewController()
playerVC.speeds = [
    AVPlaybackSpeed(rate: 0.5, localizedName: "半速"),
    AVPlaybackSpeed(rate: 1.0, localizedName: "正常"),
    AVPlaybackSpeed(rate: 1.5, localizedName: "1.5 倍速"),
    AVPlaybackSpeed(rate: 2.0, localizedName: "雙倍速")
]

使用 AVPlaybackSpeed.systemDefaultSpeeds 可恢復為預設的速度選項。

跳轉行為

設定前進/後退跳轉控制項:

playerVC.isSkipForwardEnabled = true
playerVC.isSkipBackwardEnabled = true

正在播放整合

AVPlayerViewController 預設會自動更新 MPNowPlayingInfoCenter。如果你手動管理正在播放資訊,請停用此功能:

playerVC.updatesNowPlayingInfoCenter = false

字幕與隱藏式字幕

當媒體包含適當的文字軌道時,AVKit 會自動處理字幕與隱藏式字幕的顯示。使用者可在「設定 > 輔助使用 > 字幕與隱藏式字幕」中控制字幕偏好設定。

限制字幕語言

// 僅顯示英文與西班牙文字幕選項
playerVC.allowedSubtitleOptionLanguages = ["en", "es"]

強制顯示字幕

強制字幕始終顯示(使用者無法關閉):

playerVC.requiresFullSubtitles = true

媒體選擇(委派)

回應使用者變更字幕或音訊軌道選擇的行為:

func playerViewController(
    _ playerViewController: AVPlayerViewController,
    didSelect mediaSelectionOption: AVMediaSelectionOption?,
    in mediaSelectionGroup: AVMediaSelectionGroup
) {
    if let option = mediaSelectionOption {
        print("已選擇:\(option.displayName)")
    }
}

在 HLS 中提供字幕軌道

字幕與隱藏式字幕內嵌於 HLS 清單中。AVKit 會從 AVAsset 的 AVMediaSelectionGroup 讀取。對於本機檔案,請使用 AVMutableComposition 新增 AVMediaCharacteristic.legible 軌道。

常見錯誤

錯誤:子類化 AVPlayerViewController

Apple 明確表示不支援此操作。可能導致未定義行為或未來 OS 版本崩潰。

// 錯誤
class MyPlayerVC: AVPlayerViewController { } // 不支援

// 正確:使用組合與委派
let playerVC = AVPlayerViewController()
playerVC.delegate = coordinator

錯誤:為 PiP 跳過音訊會話設定

如果音訊會話未設定為 .playback,PiP 會默默失敗。背景音訊也會停止運作。

// 錯誤:預設音訊會話
let playerVC = AVPlayerViewController()
playerVC.player = player // PiP 無法運作

// 正確:先設定音訊會話
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
try AVAudioSession.sharedInstance().setActive(true)
let playerVC = AVPlayerViewController()
playerVC.player = player

錯誤:忘記 PiP 還原委派或其完成處理常式

沒有 restoreUserInterfaceForPictureInPictureStopWithCompletionHandler,系統就無法將使用者帶回你的播放器。未呼叫 completionHandler(true) 會導致系統處於不一致狀態。

// 錯誤:沒有委派方法或缺少 completionHandler 呼叫
// 使用者在 PiP 中點擊還原 -> 無反應或動畫卡住

// 正確
func playerViewController(
    _ playerViewController: AVPlayerViewController,
    restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
    present(playerViewController, animated: false) {
        completionHandler(true)
    }
}

錯誤:在 SwiftUI 視圖的 init 中建立 AVPlayer

急切建立播放器會導致效能問題。SwiftUI 可能會多次重建視圖。

// 錯誤:每次視圖初始化時都建立
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) }
    }
}

審查清單

  • 音訊會話類別已設定為 .playback 搭配 mode: .moviePlayback
  • 已在 Info.plist 的 UIBackgroundModes 中新增 audio 背景模式
  • AVPlayerViewController 未被子類化
  • 已實作 PiP 還原委派方法並呼叫 completionHandler(true)
  • AVPlayer 已在 SwiftUI 中延遲至 .task(非急切建立)
  • 已為內嵌播放器設定 canStartPictureInPictureAutomaticallyFromInline
  • 僅在必要的廣告/法律區段切換 requiresLinearPlayback
  • 已在 AVPlayer 上啟用 allowsExternalPlayback 以支援 AirPlay
  • 已使用實際媒體軌道測試字幕語言限制
  • 已適當設定影片重力(.resizeAspect 與 .resizeAspectFill)
  • 已在顯示播放器視圖前觀察 isReadyForDisplay
  • 已處理網路串流內容的錯誤(HLS 失敗、逾時)

參考資料

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

JordanCoin/ios-skills-collection

Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries. Use when the user asks about GitHub issues, pull requests, workflows, or wants to interact with GitHub repositories from the command line — including tasks like check CI status, create PR, list issues, or query the GitHub API.

JordanCoin/ios-skills-collection

Integrate and optimize Core ML models in iOS apps for on-device machine learning inference. Covers model loading (.mlmodelc, .mlpackage), predictions with auto-generated classes and MLFeatureProvider, compute unit configuration (CPU, GPU, Neural Engine), MLTensor, VNCoreMLRequest, MLComputePlan, multi-model pipelines, and deployment strategies. Use when loading Core ML models, making predictions, configuring compute units, or profiling model performance.

JordanCoin/ios-skills-collection

Implement, review, or improve maps and location features in iOS/macOS apps using MapKit and CoreLocation. Use when working with Map views, annotations, markers, polylines, user location tracking, geocoding, reverse geocoding, search/autocomplete, directions and routes, geofencing, region monitoring, CLLocationUpdate async streams, or location authorization flows. Also use when working with maps, coordinates, addresses, places, directions, distance calculations, or location-based features in Swift apps.

JordanCoin/ios-skills-collection

Display and manipulate PDF documents using PDFKit. Use when embedding PDFView to show PDF files, creating or modifying PDFDocument instances, adding annotations (highlights, notes, signatures), extracting text with PDFSelection, navigating pages, generating thumbnails, filling PDF forms, or wrapping PDFView in SwiftUI.

JordanCoin/ios-skills-collection

Implement, review, or improve in-app tips and onboarding using Apple's TipKit framework. Use when adding feature discovery tooltips, onboarding flows, contextual tips, first-run experiences, coach marks, or working with Tip protocol, TipView, popoverTip, tip rules, tip events, or feature education UI.

JordanCoin/ios-skills-collection

When the user wants to optimize their Google Play Store listing — title, short description, full description, keywords, ratings, or Play Store-specific features. Use when the user mentions "Google Play", "Android", "Play Store", "Play Console", "short description", "full description indexed", "Google Play ASO", or wants Google Play-specific keyword, creative, or ratings strategy. For iOS App Store optimization, see aso-audit and metadata-optimization.

JordanCoin/ios-skills-collection

When the user wants to implement, optimize, or use App Clips for app discovery and conversion. Use when the user mentions "App Clip", "app clip code", "mini app", "instant app", "App Clip card", "App Clip link", "no download required", "instant experience", or wants to understand how App Clips appear in App Store search. For general App Store discoverability, see aso-audit. For marketing campaigns, see ua-campaign.

JordanCoin/ios-skills-collection

When the user wants to plan a launch strategy for a new app or major update. Also use when the user mentions "app launch", "launch plan", "launch checklist", "pre-launch", "launch day", or "how to launch my app". For ongoing ASO after launch, see aso-audit. For paid acquisition during launch, see ua-campaign.

JordanCoin/ios-skills-collection

When the user wants to analyze their own app's actual performance data from App Store Connect — real downloads, revenue, IAP, subscriptions, trials, or country breakdowns synced via Appeeky Connect. Use when the user asks about "my downloads", "my revenue", "how is my app performing", "ASC data", "sales and trends", "my subscription numbers", "App Store Connect metrics", or wants to compare periods or top markets. For third-party app estimates, see app-analytics. For subscription analytics depth, see monetization-strategy.

JordanCoin/ios-skills-collection

When the user wants a full ASO health audit, review their App Store listing quality, or diagnose why their app isn't ranking. Also use when the user mentions "ASO audit", "ASO score", "why am I not ranking", "listing review", or "optimize my app store page". For keyword-specific research, see keyword-research. For metadata writing, see metadata-optimization.

JordanCoin/ios-skills-collection

Always use when working on any iOS, macOS, Swift, SwiftUI, Xcode, or Apple platform project. Routes to the correct specialized skills based on development phase and task context. This collection contains 210 skills from 28 authors covering the full iOS lifecycle.

相關技能