Picture-in-Picture on iOS
The Picture-in-Picture (PiP) feature allows you to create a floating player. This is commonly used to let the video remain playing on screen even if:
- The user scrolls to another section of the page (causing the original player to no longer be visible).
- The user sends the application to the background.
- The user opens a new page within the same application.
THEOplayer distinguishes two flavors of Picture-in-Picture:
- With in-app Picture-in-Picture, the visibility of the PiP window is contained to the inside of the app. In other words, it goes to background and foreground together with the application.
- With out-of-app Picture-in-Picture, the visibility of the PiP window is not contained to the inside of the app. It can remain visible while the user navigates to other views, pages or apps.
Picture-in-Picture is a presentation mode of the player.
As such, you can listen for a presentationmodechange event, or read the player's presentation mode,
to find out what the presentation mode is at a given moment or to detect a change.
iOS supports both in-app and out-of-app Picture-in-Picture. tvOS only supports in-app Picture-in-Picture.
Enabling Picture-in-Picture
Instantiate the player with a PiPConfiguration, passed in the THEOplayerConfiguration.
This configuration lets you configure whether the player should retain its presentation mode upon source change,
and whether to make use of out-of-app PiP (nativePictureInPicture).
Note that the minimum required iOS version for out-of-app PiP is 14.0.
/* Configure whether presentation mode should be retained on source changes and whether to use native PiP */
let pipConfig = PiPConfiguration(retainPresentationModeOnSourceChange: true, nativePictureInPicture: true)
let playerConfig = THEOplayerConfiguration(pip: pipConfig)
let theoplayer = THEOplayer(configuration: playerConfig)
Once Picture-in-Picture is enabled, the player's pip property can be accessed.
While using in-app PiP, you can use the configure(movable:defaultCorner:) method of the player's pip property
to configure whether the PiP view is movable (defaults to true),
and in which corner the PiP view is shown by default (defaults to the bottom right corner).
theoplayer.pip!.configure(movable: false, defaultCorner: .bottomLeft)
Notes:
configureis only available when using in-app PiP.- tvOS only supports in-app PiP, which is enabled by default if the OS version supports it (minimum tvOS 14.0).
- iPadOS 14.0 (and above) also supports in-app PiP.
Observing in-app Picture-in-Picture
Using the notification center, you can listen to the PictureInPictureMoved notification.
This notification is pushed every time the Picture-in-Picture view moves to a different corner.
In the callback, you can retrieve the previous and the new corner in the userInfo dictionary,
respectively with the PictureInPictureOldCornerUserInfoKey and the PictureInPictureNewCornerUserInfoKey keys.
NotificationCenter.default.addObserver(self, selector: #selector(onPiPMoved), name: Notification.Name.PictureInPictureMoved, object: nil)
@objc func onPiPMoved(notif: Notification) {
let userInfo = notif.userInfo as! [String: Any]
let oldCorner: PictureInPictureCorner = userInfo[PictureInPictureOldCornerUserInfoKey]! as! PictureInPictureCorner
let newCorner: PictureInPictureCorner = userInfo[PictureInPictureNewCornerUserInfoKey]! as! PictureInPictureCorner
print("PiP has moved from \(oldCorner) to \(newCorner)")
}
Observing out-of-app Picture-in-Picture
By implementing your own AVPictureInPictureControllerDelegate, you are able to listen to a number of events. These contain, but are not limited to, when the player will enter or exit PiP, and when it has entered or exited PiP. To achieve this, set your implementation of the delegate as the one of the PictureInPicture controller.
class CustomPiPDelegate: NSObject, AVPictureInPictureControllerDelegate {
func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) {
// Custom action when picture in picture started
}
// ... Others here.
}
let customDelegate = CustomPiPDelegate()
let theoPiP = theoplayer.pip
theoPiP?.nativePictureInPictureDelegate = customDelegate
Known limitations
- VR360 is not supported in combination with Picture-in-Picture.
- Only server-side ad insertion is supported, no client-side ad insertion.
- Sideloaded text tracks are not supported.