garysimpson.dev
Mobile development with swift and flutter

PiP for the Win

Was tasked recently to enable PiP (Picture In Picture) mode in an application. Thanks to an awesome wwdc session video I was able to get things working smoothly.


AppDelegate+AVPlayer

import Foundation
import AVKit

// MARK: - AVPlayerViewControllerDelegate
extension AppDelegate: AVPlayerViewControllerDelegate {
    func playerViewController(_ playerViewController: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
        
        navigationController?.present(playerViewController, animated: true) {
            completionHandler(true)
        }
    }
    
    func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        
        guard playerViewController.player?.currentItem?.status != .failed else {
            playerViewController.dismiss(animated: true)
            self.presentPlaybackError()
            return
        }
    }
    
    func playerViewController(_ playerViewController: AVPlayerViewController, failedToStartPictureInPictureWithError error: Error) {
        presentPlaybackError()
    }
    
    func presentPlaybackError() {
       ///PRESENT FAILURE DIALOG HERE
    }

    
    /// Takes the given URL and plays it using **AVPlayer** in FullScreen with the PiP option when the system determines compatibility.
    func presetFullScreenVideo(_ videoUrl: URL) {
        let avPlayer = AVPlayer(url: videoUrl)
        
        avPlayer.allowsExternalPlayback = true
        
        let playerController = AVPlayerViewController.init()
        playerController.delegate = self
        playerController.allowsPictureInPicturePlayback = true
        playerController.entersFullScreenWhenPlaybackBegins = true
        playerController.canStartPictureInPictureAutomaticallyFromInline = true
        playerController.exitsFullScreenWhenPlaybackEnds = true
        playerController.player = avPlayer
        
        avPlayer.playImmediately(atRate: 1.0)
        
        navigationController?.present(playerController, animated: true, completion: {
            /// Allow the OS to attempt to display the PiP button
            do{
                try AVAudioSession.sharedInstance().setActive(true)
                try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback)
            }
            catch{}
            
            guard playerController.player?.currentItem?.status != .failed else {
                playerController.dismiss(animated: true)
                self.presentPlaybackError()
                return
            }
        })
    }
}

Happy Coding ;-)

Tagged with: