概述
本文中Demo下載地址。
如果你想自定義播放器實(shí)現(xiàn)畫(huà)中畫(huà),就可以采用 AVKit 框架中的AVPictureInPictureController
類(lèi)狗超。 如果你想基礎(chǔ)的實(shí)現(xiàn)畫(huà)中畫(huà)可以參考AVPlayerViewController實(shí)現(xiàn)畫(huà)中畫(huà) 丝格。
使用AVPictureInPictureController實(shí)現(xiàn)畫(huà)中畫(huà)步驟
-
創(chuàng)建一個(gè)項(xiàng)目唠梨,添加Background modes算芯;選擇Target > Signing & Capabilities項(xiàng)柒昏,點(diǎn)擊“+ Capability”添加Background modes,把“Audio也祠,AirPlay昙楚,and Picture in picture”打上對(duì)勾近速。如下圖:
配置Audio Playback Behavior,需要設(shè)置App 的AVAudioSession的Category為playback模式,示例代碼如下:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//需要設(shè)置App 的AVAudioSession的Category為playback模式
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playback)
} catch {
print("Setting category to AVAudioSessionCategoryPlayback failed.")
}
return true
}
- 創(chuàng)建一個(gè)新的AVPictureInPictureController對(duì)象曹货,向它傳遞一個(gè)對(duì)顯示視頻內(nèi)容的AVPlayerLayer的引用徘跪。需要對(duì)AVPictureInPictureController對(duì)象必須強(qiáng)引用才能使PiP正常功能。設(shè)置AVPictureInPictureController示例代碼如下:
var playerLayer : AVPlayerLayer = AVPlayerLayer()
var player : AVPlayer?
var pictureInPictureController : AVPictureInPictureController?
//設(shè)置PictureInPicture
func setupPictureInPicture() {
guard let videoURL = Bundle.main.url(forResource: "v1", withExtension: "MP4") else {
return
}
//設(shè)置AVPlayerLayer
playerLayer.frame = self.view.frame
playerLayer.videoGravity = .resizeAspect
self.view.layer.addSublayer(playerLayer)
//設(shè)置AVPlayer
let asset = AVURLAsset(url: videoURL)
let playerItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playerItem)
if let player = player {
playerLayer.player = player
}
// Ensure PiP is supported by current device. isPictureInPictureSupported()判斷設(shè)備是否支持畫(huà)中畫(huà)
if AVPictureInPictureController.isPictureInPictureSupported() {
// Create a new controller, passing the reference to the AVPlayerLayer.
pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer)
pictureInPictureController?.delegate = self //設(shè)置代理
} else {
print("當(dāng)前設(shè)備不支持PiP")
}
player?.play()
}
- 使用AVPictureInPictureController的pictureInPictureButtonStartImage和pictureInPictureButtonStopImage析砸,我們可以創(chuàng)建一個(gè)Button昔字,然后把Button的image設(shè)置為pictureInPictureButtonStartImage或者pictureInPictureButtonStopImage。示例代碼如下:
func addPipButton() {
//設(shè)置開(kāi)啟或者關(guān)閉pictureInPicture
pipButton = UIButton(type: .system)
//系統(tǒng)默認(rèn)的pictureInPictureButtonStartImage
let startImage = AVPictureInPictureController.pictureInPictureButtonStartImage
pipButton.setImage(startImage, for: .normal)
pipButton.frame = CGRect(x: 10, y: 20, width: 40, height: 40)
pipButton.addTarget(self, action: #selector(clickPipButton(_:)), for: .touchUpInside)
self.view.addSubview(pipButton)
}
@objc func clickPipButton(_ sender: Any) {
//判斷Pip是否在Active狀態(tài)
guard let isActive = pictureInPictureController?.isPictureInPictureActive else { return }
if (isActive) {
//停止畫(huà)中畫(huà)
pictureInPictureController?.stopPictureInPicture()
let startImage = AVPictureInPictureController.pictureInPictureButtonStartImage
pipButton.setImage(startImage, for: .normal)
} else {
//啟動(dòng)畫(huà)中畫(huà)
pictureInPictureController?.startPictureInPicture()
//系統(tǒng)默認(rèn)的pictureInPictureButtonStopImage
let stopImage = AVPictureInPictureController.pictureInPictureButtonStopImage
pipButton.setImage(stopImage, for: .normal)
}
}
-
然后 build and run你的項(xiàng)目即可實(shí)現(xiàn)一個(gè)自定義的畫(huà)中畫(huà)Demo首繁。
效果圖如下:
AVPictureInPictureController的一些常用方法:
//開(kāi)始啟動(dòng)畫(huà)中畫(huà)
open func startPictureInPicture()
//停止畫(huà)中畫(huà)
open func stopPictureInPicture()
//當(dāng)前是否可以使用畫(huà)中畫(huà)
open var isPictureInPicturePossible: Bool { get }
//當(dāng)前是否處于畫(huà)中畫(huà)狀態(tài)
open var isPictureInPictureActive: Bool { get }
//當(dāng)前畫(huà)中畫(huà)是否被掛起
open var isPictureInPictureSuspended: Bool { get }
//這可用于臨時(shí)強(qiáng)制播放強(qiáng)制內(nèi)容(如法律術(shù)語(yǔ)或廣告)
@available(iOS 14.0, *)
open var requiresLinearPlayback: Bool
AVPictureInPictureControllerDelegate的代理方法
extension ViewController : AVPictureInPictureControllerDelegate {
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("將要開(kāi)始PictureInPicture的代理方法")
}
func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("已經(jīng)開(kāi)始PictureInPicture的代理方法")
}
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
print("啟動(dòng)PictureInPicture失敗的代理方法")
}
func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("將要停止PictureInPicture的代理方法")
}
func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("已經(jīng)停止PictureInPicture的代理方法")
}
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
//此方法執(zhí)行在pictureInPictureControllerWillStopPictureInPicture代理方法之后作郭,在pictureInPictureControllerDidStopPictureInPicture執(zhí)行之前。 但是點(diǎn)擊“X”移除畫(huà)中畫(huà)時(shí)弦疮,不執(zhí)行此方法夹攒。
print("PictureInPicture停止之前恢復(fù)用戶界面")
}
}