Search code examples
iosavplayerviewcontrolleravkitpicture-in-picture

iOS AVPlayerViewController distinguish user actions to exit Picture-in-Picture mode


In my iOS Video Player, when in the Picture-in-Picture mode (PIP), how can I handle specific user actions like close or restore. The existing delegate provides a way to know when PIP mode is stopped but I wan't different behavior to be executed on different user actions:

  • close - let the controller go and keep it closed
  • restore - re-present the player and continue playback

enter image description here

After implementing AVPlayerViewControllerDelegate, AVPlayerViewController calls the following functions regardless of what button user tapped:

playerViewControllerWillStopPictureInPicture
playerViewControllerDidStopPictureInPicture

I also defined method restoreUserInterfaceForPictureInPictureStopWithCompletionHandler but it seems not getting called for some reason on exit from the PIP mode.

    func restoreUserInterfaceForFullScreenExit(completionHandler: @escaping (Bool) -> Void) {
        completionHandler(true)
    }
    
    func restoreUserInterfaceForPictureInPictureStop(completionHandler: @escaping (Bool) -> Void) {
        completionHandler(true)
    }

Solution

  • In general I have view that contained AVPLayer playerView and it has delegate:

    var pipControllerDelegate: AVPictureInPictureControllerDelegate?
    playerView.pipControllerDelegate = self
    

    I've implemented required methods by the following way and it works for me:

    extension PlayerView: AVPictureInPictureControllerDelegate {
    func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
        close()
    }
    
    func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController,
                                      restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
            let isPipPlaying = (pictureInPictureController.playerLayer.player?.timeControlStatus == .playing)
            if isPipPlaying {
                play()
            } else {
                paused()
            }
            completionHandler(true)
    }
    

    May be your delegate bacomes nil at some place of code?