Communitygithub.com

JordanCoin/ios-skills-collection

AVKit을 사용하여 미디어 재생 경험을 생성합니다. AVPlayerViewController로 비디오 플레이어를 추가하거나, PIP(Picture-in-Picture)를 활성화하거나, AirPlay로 미디어를 라우팅하거나, SwiftUI VideoPlayer 뷰를 사용하거나, 전송 컨트롤을 구성하거나, 자막 및 폐쇄 자막을 표시하거나, AVFoundation 재생을 시스템 UI와 통합할 때 사용하세요.

ios-skills-collection란 무엇인가요?

이 스킬은 AVFoundation을 기반으로 한 고수준 미디어 재생 UI인 AVKit을 활용하여 iOS 앱에서 비디오 재생 환경을 구축하는 방법을 가르칩니다. AVPlayerViewController를 사용한 전체 화면 및 인라인 임베디드 재생, SwiftUI VideoPlayer 뷰, PiP(Picture-in-Picture), AirPlay 라우팅, 전송 컨트롤 및 재생 속도 조절, 자막과 폐쇄 자막 표시 등 주요 기능을 다룹니다. Swift 6.3 및 iOS 26+를 대상으로 하며, 오디오 세션 구성 및 오디오 백그라운드 모드 추가와 같은 필수 설정부터 시작하여 라이브러리 가져오기, 델리게이트 패턴, 표시 준비 상태 관찰 등의 실용적인 구현 예제를 제공합니다. 또한 ViewController보다는 시스템 AVPlayerViewController 사용을 강조하고, 직접 서브클래싱을 피하도록 권장합니다. 사용자는 이 스킬을 통해 Apple의 표준 재생 UI를 빠르게 통합하고, PIP나 AirPlay와 같은 고급 기능을 최소한의 코드로 활성화할 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/JordanCoin/ios-skills-collection/tree/HEAD/skills/dpearson2699--avkit

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

AVKit

AVFoundation 기반의 고급 미디어 재생 UI입니다. 시스템 표준 비디오 플레이어, PIP(Picture-in-Picture), 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에서 확인하세요.

Picture-in-Picture

PiP를 사용하면 사용자가 다른 앱을 사용하는 동안 부동 창에서 비디오를 시청할 수 있습니다. AVPlayerViewController는 오디오 세션이 구성되면 자동으로 PiP를 지원합니다. 사용자 정의 플레이어 UI의 경우 AVPictureInPictureController를 직접 사용하세요.

전제 조건

  1. 오디오 세션 카테고리가 .playback으로 설정됨 (설정 참조)
  2. UIBackgroundModes에 audio 백그라운드 모드

표준 플레이어 PiP

PiP는 AVPlayerViewController에서 기본적으로 활성화됩니다. 자동 활성화 및 인라인-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: "2배속")
]

AVPlaybackSpeed.systemDefaultSpeeds를 사용하여 기본 속도 옵션을 복원하세요.

건너뛰기 동작

앞으로/뒤로 건너뛰기 컨트롤을 구성하세요.

playerVC.isSkipForwardEnabled = true
playerVC.isSkipBackwardEnabled = true

Now Playing 통합

AVPlayerViewController는 기본적으로 MPNowPlayingInfoCenter를 자동으로 업데이트합니다. Now Playing 정보를 수동으로 관리하는 경우 이 기능을 비활성화하세요.

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는 뷰를 여러 번 다시 생성할 수 있습니다.

// 잘못됨: 모든 뷰 init에서 생성됨
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 사용
  • UIBackgroundModes에 audio 백그라운드 모드가 Info.plist에 추가됨
  • AVPlayerViewController가 서브클래싱되지 않음
  • PiP 복원 델리게이트 메서드가 구현되고 completionHandler(true) 호출
  • SwiftUI에서 AVPlayer가 .task로 지연됨 (미리 생성되지 않음)
  • 인라인 플레이어에 대해 canStartPictureInPictureAutomaticallyFromInline이 설정됨
  • requiresLinearPlayback이 필요한 광고/법적 구간에서만 토글됨
  • AirPlay 지원을 위해 AVPlayer에서 allowsExternalPlayback이 활성화됨
  • 자막 언어 제한이 실제 미디어 트랙으로 테스트됨
  • 비디오 중력이 적절하게 설정됨 (.resizeAspect vs .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.

관련 스킬