Article

Adopting Picture in Picture in a Custom Player

Add picture-in-picture controls to your custom player using a picture-in-picture controller.

Overview

Add picture-in-picture playback to your custom player by using the AVKit framework’s AVPictureInPictureController class. This class lets you implement the same picture-in-picture behavior found in AVPlayerViewController, but requires some additional work from you.

Add a UI to Your Custom Player

Begin by adding a UI to your custom player interface to enable users to begin picture-in-picture playback. Make this UI consistent with the system default UI presented by AVPlayerViewController. Access the standard images for controlling picture-in-picture playback by using the pictureInPictureButtonStartImage(compatibleWith:) and pictureInPictureButtonStopImage(compatibleWith:) class methods of AVPictureInPictureController. These methods return system default images to present in your UI.

@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var stopButton: UIButton!

override func viewDidLoad() {
    super.viewDidLoad()

    let startImage = AVPictureInPictureController.pictureInPictureButtonStartImage(compatibleWith: nil)
    let stopImage = AVPictureInPictureController.pictureInPictureButtonStopImage(compatibleWith: nil)

    startButton.setImage(startImage, for: .normal)
    stopButton.setImage(stopImage, for: .normal)
}

Create the Picture-in-Picture Controller

You create an instance of AVPictureInPictureController to control picture-in-picture playback in your app. Before attempting to create the AVPictureInPictureController instance, verify that picture-in-picture is supported by the current hardware using the isPictureInPictureSupported() method.

func setupPictureInPicture() {
    // Ensure PiP is supported by current device
    if AVPictureInPictureController.isPictureInPictureSupported() {
        // Create new controller passing reference to the AVPlayerLayer
        pictureInPictureController = AVPictureInPictureController(playerLayer: playerLayer)
        pictureInPictureController.delegate = self
        let keyPath = #keyPath(AVPictureInPictureController.isPictureInPicturePossible)
        pictureInPictureController.addObserver(self,
                                               forKeyPath: keyPath,
                                               options: [.initial, .new],
                                               context: &pictureInPictureControllerContext)
    } else {
        // PiP not supported by current device. Disable PiP button.
        pictureInPictureButton.isEnabled = false
    }
}

This example creates a new instance of AVPictureInPictureController, passing it a reference to the AVPlayerLayer used to present the video content. You need to maintain a strong reference to the controller object for picture-in-picture functionality to work.

To participate in picture-in-picture life-cycle events, your code should adopt the AVPictureInPictureControllerDelegate protocol and set itself as the controller’s delegate. Also, use key-value observing (KVO) on the controller’s isPictureInPicturePossible property. This property indicates whether using picture-in-picture mode is possible in the current context; for example, when an active FaceTime window is displayed. Observing this property enables you to determine when it’s appropriate to change the enabled state of your picture-in-picture button.

Handle User-Initiated Requests

With the AVPictureInPictureController setup complete, add an @IBAction method to handle user-initiated requests to start or stop picture-in-picture playback.

@IBAction func togglePictureInPictureMode(_ sender: UIButton) {
    if pictureInPictureController.isPictureInPictureActive {
        pictureInPictureController.stopPictureInPicture()
    } else {
        pictureInPictureController.startPictureInPicture()
    }
}

Restore Control to Your App

A user taps the button in the picture-in-picture window to return control to your app. By default, this action terminates playback when control returns to the app. AVKit can’t make assumptions about how your app is structured and is unaware of how to properly restore your video playback interface. Instead, it delegates that responsibility to you.

To handle the restore process, implement the pictureInPictureController(_:restoreUserInterfaceForPictureInPictureStopWithCompletionHandler:) delegate method and restore your player interface as needed. When the restoration is complete, call the completion handler with a value of true.

func picture(_ pictureInPictureController: AVPictureInPictureController,
             restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
    // Restore user interface
    completionHandler(true)
}

Dismiss Playback Controls

While picture-in-picture mode is active, dismiss playback controls in your main player and present artwork inside its bounds to indicate that picture-in-picture mode is under way. To implement this functionality, use the pictureInPictureControllerWillStartPictureInPicture(_:) and pictureInPictureControllerDidStopPictureInPicture(_:) delegate methods and take the required actions.

func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
    // hide playback controls
    // show placeholder artwork
}

func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
    // hide placeholder artwork
    // show playback controls
}

See Also

Media Playback

Adopting Picture in Picture in a Standard Player

Add picture-in-picture controls to your player using a player view controller.

Working with Interstitial Content (tvOS)

Present additional content such as legal text, content warnings, or advertisements alongside your main media content.

Adding Information to the Info Panel (tvOS)

Add metadata information and navigation markers to the Info panel in Apple TV.

Adopting Custom Interactive Overlays, Channel Flipping, and Parental Controls in tvOS Video Playback

This sample demonstrates support for custom interactive overlays, and parental controls, using AVPlayerViewController.

class AVPlayerViewController

An object that displays the video content from a player object along with system-supplied playback controls.

class AVPlayerView

A macOS-only object that displays the video content from a player object along with system-supplied playback controls.

class AVPictureInPictureController

A controller that responds to user-initiated playback of video in a floating, resizable window on iPad.

class AVRoutePickerView

An object that displays controls for picking playback routes.

class AVInterstitialTimeRange

A time range in an audiovisual presentation as interstitial content, such as advertisements or legal notices.

class AVNavigationMarkersGroup

A set of markers for navigating playback of an audiovisual presentation.