You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 15:07:50