Communitygithub.com

JordanCoin/ios-skills-collection

使用 AVKit 创建媒体播放体验。适用于添加带有 AVPlayerViewController 的视频播放器、启用画中画、通过 AirPlay 路由媒体、使用 SwiftUI VideoPlayer 视图、配置传输控件、显示字幕和隐藏式字幕,或集成 AVFoundation 播放与系统 UI。

ios-skills-collection 是什么?

该技能专注于 iOS 平台上基于 AVKit 框架的高层媒体播放 UI 实现,是 AVFoundation 的系统标准封装。它提供了完整的视频播放解决方案,包括 AVPlayerViewController 的全屏与内嵌播放、SwiftUI 的 VideoPlayer 视图、画中画 (PiP) 功能、AirPlay 路由、自定义传输控件和播放速度、以及字幕/隐藏式字幕的支持。技能内容面向 Swift 6.3 和 iOS 26+,详细说明了音频会话配置、关键属性、委托方法和常见陷阱(如禁止子类化 AVPlayerViewController、必须配置音频会话以支持 PiP、不要在 SwiftUI 的 init 中创建 AVPlayer 等)。此外,它还提供了可选语言的字幕限制、强制字幕显示、媒体选择委托等高级定制选项。该技能适用于所有使用 AVKit 的 iOS 开发场景,无论是 UIKit 还是 SwiftUI,都能帮助开发者快速实现符合系统标准的媒体播放功能。

兼容平台~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。提供系统标准的视频播放器、画中画 (Picture-in-Picture)、AirPlay 路由、传输控件以及字幕/隐藏式字幕显示。目标平台:Swift 6.3 / iOS 26+。

目录

设置

音频会话配置

在开始任何播放之前,先配置音频会话和后台模式。否则,画中画和后台音频将静默失败。

  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 播放器。它开箱即用地提供系统播放控件、画中画、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 在视频和传输控件之间添加非交互式视图(水印、Logo)。

委托

采用 AVPlayerViewControllerDelegate 以响应全屏切换、画中画生命周期事件、插播播放和媒体选择更改。使用过渡协调器的 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 的属性。对于画中画配置、委托回调或播放速度控制,将 AVPlayerViewController 封装在 UIViewControllerRepresentable 中。完整模式请参见 references/avkit-patterns.md。

画中画 (Picture-in-Picture)

画中画允许用户在浮动窗口中观看视频,同时使用其他应用。一旦音频会话配置完成,AVPlayerViewController 会自动支持画中画。对于自定义播放器 UI,直接使用 AVPictureInPictureController。

先决条件

  1. 音频会话类别设置为 .playback(参见设置)
  2. 在 UIBackgroundModes 中添加 audio 后台模式

标准播放器画中画

AVPlayerViewController 默认启用了画中画。控制自动激活和内嵌到画中画的切换:

let playerVC = AVPlayerViewController()
playerVC.player = player

// 画中画默认启用;设置为 false 可禁用
playerVC.allowsPictureInPicturePlayback = true

// 当应用进入后台时自动启动画中画(适用于内嵌/非全屏播放器)
playerVC.canStartPictureInPictureAutomaticallyFromInline = true

画中画停止时恢复 UI

当用户点击画中画中的恢复按钮时,实现委托方法来重新呈现播放器。调用完成处理程序并传递 true,以通知系统完成恢复动画。

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

自定义播放器画中画

对于自定义播放器 UI,使用带有 AVPlayerLayer 或采样缓冲区内容源的 AVPictureInPictureController。首先检查 isPictureInPictureSupported()。有关完整的自定义播放器和采样缓冲区画中画模式,请参见 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

不要:为画中画跳过音频会话配置

如果音频会话未设置为 .playback,画中画会静默失败。后台音频也会停止工作。

// 错误:默认音频会话
let playerVC = AVPlayerViewController()
playerVC.player = player // 画中画无法工作

// 正确:先配置音频会话
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .moviePlayback)
try AVAudioSession.sharedInstance().setActive(true)
let playerVC = AVPlayerViewController()
playerVC.player = player

不要:忘记画中画恢复委托或其完成处理程序

如果没有 restoreUserInterfaceForPictureInPictureStopWithCompletionHandler,系统无法将用户返回到你的播放器。未能调用 completionHandler(true) 会使系统处于不一致状态。

// 错误:没有委托方法或缺少 completionHandler 调用
// 用户在画中画中点击恢复 -> 无响应或动画挂起

// 正确
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
  • 实现了画中画恢复委托方法并调用了 completionHandler(true)
  • 在 SwiftUI 中将 AVPlayer 延迟到 .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.

相关技能