概述
本文中Demo下載地址。
如果你想自定義播放器實現(xiàn)畫中畫,就可以采用 AVKit 框架中的AVPictureInPictureController類。 如果你想基礎(chǔ)的實現(xiàn)畫中畫可以參考AVPlayerViewController實現(xiàn)畫中畫 。
使用AVPictureInPictureController實現(xiàn)畫中畫步驟
-
創(chuàng)建一個項目,添加Background modes;選擇Target > Signing & Capabilities項,點擊“+ Capability”添加Background modes,把“Audio,AirPlay,and Picture in picture”打上對勾。如下圖:
23.png 配置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)建一個新的AVPictureInPictureController對象,向它傳遞一個對顯示視頻內(nèi)容的AVPlayerLayer的引用。需要對AVPictureInPictureController對象必須強引用才能使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è)備是否支持畫中畫
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)建一個Button,然后把Button的image設(shè)置為pictureInPictureButtonStartImage或者pictureInPictureButtonStopImage。示例代碼如下:
func addPipButton() {
//設(shè)置開啟或者關(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) {
//停止畫中畫
pictureInPictureController?.stopPictureInPicture()
let startImage = AVPictureInPictureController.pictureInPictureButtonStartImage
pipButton.setImage(startImage, for: .normal)
} else {
//啟動畫中畫
pictureInPictureController?.startPictureInPicture()
//系統(tǒng)默認(rèn)的pictureInPictureButtonStopImage
let stopImage = AVPictureInPictureController.pictureInPictureButtonStopImage
pipButton.setImage(stopImage, for: .normal)
}
}
-
然后 build and run你的項目即可實現(xiàn)一個自定義的畫中畫Demo。
效果圖如下:
25.PNG
AVPictureInPictureController的一些常用方法:
//開始啟動畫中畫
open func startPictureInPicture()
//停止畫中畫
open func stopPictureInPicture()
//當(dāng)前是否可以使用畫中畫
open var isPictureInPicturePossible: Bool { get }
//當(dāng)前是否處于畫中畫狀態(tài)
open var isPictureInPictureActive: Bool { get }
//當(dāng)前畫中畫是否被掛起
open var isPictureInPictureSuspended: Bool { get }
//這可用于臨時強制播放強制內(nèi)容(如法律術(shù)語或廣告)
@available(iOS 14.0, *)
open var requiresLinearPlayback: Bool
AVPictureInPictureControllerDelegate的代理方法
extension ViewController : AVPictureInPictureControllerDelegate {
func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("將要開始PictureInPicture的代理方法")
}
func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
print("已經(jīng)開始PictureInPicture的代理方法")
}
func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) {
print("啟動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í)行之前。 但是點擊“X”移除畫中畫時,不執(zhí)行此方法。
print("PictureInPicture停止之前恢復(fù)用戶界面")
}
}
