如何在Swift中实现支持画中画与后台播放的自定义视频播放器?
Swift实现支持画中画与后台播放的自定义视频播放器方案
一、前置配置(必须完成)
先在项目的Info.plist中添加以下配置:
- 添加
Required background modes,勾选App plays audio or streams audio/video using AirPlay - 添加
NSPictureInPictureUsageDescription,填写画中画功能的使用说明(比如"需要使用画中画模式继续播放视频") - 项目target -> Signing & Capabilities -> 添加Background Modes,勾选
Audio, AirPlay, and Picture in Picture
二、核心实现思路
以AVPlayer作为播放内核,自定义UIView承载AVPlayerLayer,配合AVPictureInPictureController实现画中画;通过AVAudioSession配置后台音频权限,确保锁屏/切应用时音频持续播放。
三、代码示例
1. 自定义播放器视图
import UIKit import AVKit class CustomPlayerView: UIView { override class var layerClass: AnyClass { return AVPlayerLayer.self } var playerLayer: AVPlayerLayer { return layer as! AVPlayerLayer } var player: AVPlayer? { get { playerLayer.player } set { playerLayer.player = newValue } } }
2. 视图控制器完整实现
import UIKit import AVKit class PlayerViewController: UIViewController { private let playerView = CustomPlayerView() private var player: AVPlayer! private var pipController: AVPictureInPictureController? private var audioSession: AVAudioSession! override func viewDidLoad() { super.viewDidLoad() setupPlayer() setupPIP() setupAudioSession() addObservers() } private func setupPlayer() { // 替换为你的视频URL guard let videoURL = URL(string: "https://example.com/your-video.mp4") else { return } let playerItem = AVPlayerItem(url: videoURL) player = AVPlayer(playerItem: playerItem) playerView.frame = view.bounds playerView.player = player playerView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(playerView) // 添加播放控制按钮(可选) let playButton = UIButton(type: .system) playButton.setTitle("播放", for: .normal) playButton.addTarget(self, action: #selector(togglePlay), for: .touchUpInside) playButton.frame = CGRect(x: view.center.x - 50, y: view.center.y - 25, width: 100, height: 50) view.addSubview(playButton) } private func setupPIP() { guard AVPictureInPictureController.isPictureInPictureSupported() else { print("设备不支持画中画") return } pipController = AVPictureInPictureController(playerLayer: playerView.playerLayer) pipController?.delegate = self } private func setupAudioSession() { audioSession = AVAudioSession.sharedInstance() do { try audioSession.setCategory(.playback, mode: .moviePlayback, options: .mixWithOthers) try audioSession.setActive(true) } catch { print("音频会话配置失败:\(error.localizedDescription)") } } private func addObservers() { NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil) } @objc private func togglePlay() { if player.timeControlStatus == .playing { player.pause() } else { player.play() // 播放时可主动触发画中画(可选) pipController?.startPictureInPicture() } } @objc private func appDidEnterBackground() { if player.timeControlStatus == .playing { audioSession.setActive(true) } } @objc private func appWillEnterForeground() { audioSession.setActive(true) } deinit { NotificationCenter.default.removeObserver(self) player.pause() player.replaceCurrentItem(with: nil) audioSession.setActive(false) } } // MARK: - AVPictureInPictureControllerDelegate extension PlayerViewController: AVPictureInPictureControllerDelegate { func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { // 画中画启动前的自定义逻辑 } func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { player.pause() } }
四、关键注意点
- 你之前尝试的
AVPlayerViewController其实支持画中画,只需开启Background Modes权限并设置allowsPictureInPicturePlayback = true即可,大概率是之前配置缺失导致的问题 - 后台播放的核心是
AVAudioSession配置为.playback类别,确保应用在后台时音频会话保持活跃 - 画中画仅支持iOS 9+,部分老设备(如iPhone SE第一代)不支持,需做兼容性判断
内容的提问来源于stack exchange,提问作者Asif mimi
相关产品推荐
相关产品推荐

