Loading...
Loading...
Compare original and translation side by side
UIBackgroundModesaudio.playbackimport 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)")
}
}audioUIBackgroundModes.playbackimport 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)")
}
}import AVKit // AVPlayerViewController, VideoPlayer, PiP
import AVFoundation // AVPlayer, AVPlayerItem, AVAssetimport AVKit // AVPlayerViewController, VideoPlayer, PiP
import AVFoundation // AVPlayer, AVPlayerItem, AVAssetAVPlayerViewControllerAVPlayerViewControllerimport 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()
}
}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:)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)
}AVPlayerViewControlleraddChilddidMove(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 // Show/hide system controls
playerVC.videoGravity = .resizeAspect // .resizeAspectFill to crop
playerVC.entersFullScreenWhenPlaybackBegins = false
playerVC.exitsFullScreenWhenPlaybackEnds = true
playerVC.updatesNowPlayingInfoCenter = true // Auto-updates MPNowPlayingInfoCentercontentOverlayViewplayerVC.showsPlaybackControls = true // Show/hide system controls
playerVC.videoGravity = .resizeAspect // .resizeAspectFill to crop
playerVC.entersFullScreenWhenPlaybackBegins = false
playerVC.exitsFullScreenWhenPlaybackEnds = true
playerVC.updatesNowPlayingInfoCenter = true // Auto-updates MPNowPlayingInfoCentercontentOverlayViewAVPlayerViewControllerDelegateanimate(alongsideTransition:completion:)AVPlayerViewControllerDelegateanimate(alongsideTransition:completion:)isReadyForDisplaylet observation = playerVC.observe(\.isReadyForDisplay) { observed, _ in
if observed.isReadyForDisplay {
// Safe to show the player view
}
}isReadyForDisplaylet observation = playerVC.observe(\.isReadyForDisplay) { observed, _ in
if observed.isReadyForDisplay {
// Safe to show the player view
}
}VideoPlayerVideoPlayerimport 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)
}
}
}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)
}
}
}VideoPlayer(player: player) {
VStack {
Spacer()
HStack {
Image("logo")
.resizable()
.frame(width: 40, height: 40)
.padding()
Spacer()
}
}
}VideoPlayer(player: player) {
VStack {
Spacer()
HStack {
Image("logo")
.resizable()
.frame(width: 40, height: 40)
.padding()
Spacer()
}
}
}VideoPlayerAVPlayerViewControllerAVPlayerViewControllerUIViewControllerRepresentableVideoPlayerAVPlayerViewControllerAVPlayerViewControllerUIViewControllerRepresentableAVPlayerViewControllerAVPictureInPictureControllerAVPlayerViewControllerAVPictureInPictureController.playbackUIBackgroundModesaudio.playbackaudioUIBackgroundModesAVPlayerViewControllerlet playerVC = AVPlayerViewController()
playerVC.player = player
// 画中画默认启用;设置为false可禁用
playerVC.allowsPictureInPicturePlayback = true
// 当应用进入后台时自动启动画中画(适用于内嵌/非全屏播放器)
playerVC.canStartPictureInPictureAutomaticallyFromInline = trueAVPlayerViewControllerlet 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 = truetruefunc playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
// Re-present or re-embed the player view controller
present(playerViewController, animated: false) {
completionHandler(true)
}
}truefunc playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
// Re-present or re-embed the player view controller
present(playerViewController, animated: false) {
completionHandler(true)
}
}AVPlayerLayerAVPictureInPictureControllerisPictureInPictureSupported()guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
let pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = trueAVPictureInPictureControllerAVPlayerLayerisPictureInPictureSupported()guard AVPictureInPictureController.isPictureInPictureSupported() else { return }
let pipController = AVPictureInPictureController(playerLayer: playerLayer)
pipController.delegate = self
pipController.canStartPictureInPictureAutomaticallyFromInline = truerequiresLinearPlayback// 广告播放期间
playerVC.requiresLinearPlayback = true
// 广告播放完成后
playerVC.requiresLinearPlayback = falserequiresLinearPlayback// During an ad
playerVC.requiresLinearPlayback = true
// After the ad completes
playerVC.requiresLinearPlayback = falseAVPlayerViewControllerAVPlayerViewControllerimport 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)
}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)
}AVPlayerplayer.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = trueAVPlayerplayer.allowsExternalPlayback = true
player.usesExternalPlaybackWhileExternalScreenIsActive = truelet 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.systemDefaultSpeedslet 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.systemDefaultSpeedsplayerVC.isSkipForwardEnabled = true
playerVC.isSkipBackwardEnabled = trueplayerVC.isSkipForwardEnabled = true
playerVC.isSkipBackwardEnabled = trueAVPlayerViewControllerMPNowPlayingInfoCenterplayerVC.updatesNowPlayingInfoCenter = falseAVPlayerViewControllerMPNowPlayingInfoCenterplayerVC.updatesNowPlayingInfoCenter = false// 仅显示英文和西班牙文字幕选项
playerVC.allowedSubtitleOptionLanguages = ["en", "es"]// Only show English and Spanish subtitle options
playerVC.allowedSubtitleOptionLanguages = ["en", "es"]playerVC.requiresFullSubtitles = trueplayerVC.requiresFullSubtitles = truefunc playerViewController(
_ playerViewController: AVPlayerViewController,
didSelect mediaSelectionOption: AVMediaSelectionOption?,
in mediaSelectionGroup: AVMediaSelectionGroup
) {
if let option = mediaSelectionOption {
print("Selected: \(option.displayName)")
}
}func playerViewController(
_ playerViewController: AVPlayerViewController,
didSelect mediaSelectionOption: AVMediaSelectionOption?,
in mediaSelectionGroup: AVMediaSelectionGroup
) {
if let option = mediaSelectionOption {
print("Selected: \(option.displayName)")
}
}AVAssetAVMediaSelectionGroupAVMutableCompositionAVMediaCharacteristic.legibleAVMediaSelectionGroupAVAssetAVMutableCompositionAVMediaCharacteristic.legible// 错误示例
class MyPlayerVC: AVPlayerViewController { } // 不被支持
// 正确做法:使用组合模式+代理
let playerVC = AVPlayerViewController()
playerVC.delegate = coordinator// WRONG
class MyPlayerVC: AVPlayerViewController { } // Unsupported
// CORRECT: Use composition with delegation
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.playback// 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 = playerrestoreUserInterfaceForPictureInPictureStopWithCompletionHandlercompletionHandler(true)// 错误示例:没有实现代理方法或未调用完成处理程序
// 用户点击画中画的恢复按钮 -> 无反应或动画卡住
// 正确做法
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void
) {
present(playerViewController, animated: false) {
completionHandler(true)
}
}restoreUserInterfaceForPictureInPictureStopWithCompletionHandlercompletionHandler(true)// 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)
}
}// 错误示例:每次视图初始化时创建
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) }
}
}// 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: .moviePlaybackUIBackgroundModesaudioAVPlayerViewControllercompletionHandler(true).taskAVPlayercanStartPictureInPictureAutomaticallyFromInlinerequiresLinearPlaybackAVPlayerallowsExternalPlayback.resizeAspect.resizeAspectFillisReadyForDisplay.playbackmode: .moviePlaybackaudioUIBackgroundModesAVPlayerViewControllercompletionHandler(true)AVPlayer.taskcanStartPictureInPictureAutomaticallyFromInlinerequiresLinearPlaybackallowsExternalPlaybackAVPlayer.resizeAspect.resizeAspectFillisReadyForDisplay