π 2020.6.4 (THU)
π WWDC2019 | Session : 503 | Category : AVKit
π https://developer.apple.com/videos/play/wwdc2019/503/
π AVKit is a high-level framework for building media user interfaces, complete with playback controls, chapter navigation, Picture-in-Picture, audio routing, support for subtitles and closed captioning, Siri and Now Playing integration, and support for keyboard, Touch Bar, and remote control. Learn best practices in how to integrate these technologies into your own apps on iOS, tvOS, and iPad apps on Mac.
Media Playback with AVPlayerViewController on iOS and tvOS
import AVKit
// 1) Create an AVPlayer
let player = AVPlayer(url: "http://my.example/video.m3u8")
// 2) Create an AVPlayerViewController
let playerViewController = AVPlayerViewController()
playerViewController.player = player
// 3) Show it
present(playerViewController, animated: true)
When you use AVKit, you own the media playback objects. You are in control of creating and managing them.
And video rendering is build in on top of same core technology that powers AVPlayerLayer.
-
Extends
AVPlayerViewControllerDelegate
-
Notifies delegate when beginning or ending full screen presentation
@available(iOS 12.0, *) func playerViewController(_ playerViewController: AVPlayerViewController, willBeginFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator)
Implementing AVPlayerControllerDelegate's full screen callbacks
fund playerViewController(_ playerViewController: AVPlayerViewController,
willBeginFullScreenPresentationWithAnimationCoordinator coordinator:
UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: { (context) in
// Add coordinated animations
)} { (context) in
if context.isCancelled {
// Still embedded inline
} else {
// Presented full screen
// Take strong reference to playerViewControllelr if needed
}
}
alongsideTransition
calback
β This is where you find out whether a transition succeeded or whether the user cancelled. This is the source of truth for learning about entering full screen from an inline presentation.
-
Keep embedded playerViewController alive when full screen
- Deallocating will stop full screen playback
- Okay to be offscreen or removed from superview / parent
-
Use delegate to restore playerViewController or retarget animation
func playerViewController(_ playerViewController: AVPlayerViewController, willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) { // If scrolled away, update playerViewController's layout here }
-
Track full screen presentation state with
AVPlayerViewControllerDelegate
- Do not rely on
viewWillAppear(_:)
and friends
- Do not rely on
-
Works for both embedded and full screen presentations
You get the same UI that you expect from Apple's playback UIs with all the functionality that AVPlayerController delivers for iPad apps.
But you also get some platform specific features for Mac. (touch bar support, keyboard support, picture-in-picture support)
β How many new lines of code? 0!
- Exact same API an os iOS
- Picture-in-picture support
- Includes AVPictureInPictureController (also for AppKit)
- And AVPlayerView, for AppKit-based apps
- Touch Bar, keyboard, and Now Playing support
- Audio and AirPlay routing
- Available in macOS 10.15
- Interactive dismissals
- Landscape support for portrait-only apps
- Keyboard and Touch Bar support
- Now Playing management
- Automatic video zoom
- Set
showsPlaybackControls
tofalse
- Present modally
- Add controls to
contentOverlayView
- For the best user experience, you should:
- Override UIViewController methods for status bar, home inidicator
- Pass unhandled touches through your view
- Let AVKit handle double-tap for video zoom
- Covers
UIWindowScene
coordinate space - Use cases:
- Splash screen
- Full screen playback
UIViewController containment API
-
Add as child:
parent.addChild(playerViewController) parent.view.addSubView(playerViewControler.view) // Or other UIView insertion API //Enable auto layout and set up constraints playerViewController.didMove(toParent: parent)
-
Remove from parent:
playerViewController.willMove(toParent: nil) playerViewControoler.view.removeFromSuperview() playerViewController.removeFromParent()
Configure AVPlayerViewController
-
Disable playback controls:
playerViewController.showsPlyabackControls = false
-
Make video fill entire screen:
playerViewController.videoGravity = .resizeAspectFill
-
Set background color (if needed):
playerViewController.view.backgroundColor = .clear
AVFoundation best practices
- Disable
AVPlayer.allowsExternalPlayback
- Configure
AVAudioSession
for secondary media playback- Use
.ambient
category
- Use
- Observe
AVAudioSession.silenceSecondaryAudioHintNotification
- Honor
AVAudioSession.secondaryAudioShouldBeSilencedHint
- Honor
AVPlayerViewController best practices
-
Present modally (not as childViewController of other ViewController)
-
Use the default
modalPresentationStyle
-
Do not set
AVPlayerViewController.videoGravity
-
Use
AVPlayerViewController
to track full screen presentation state- Do not rely on
viewWillAppear(:_)
and friends
- Do not rely on
-
Set up
AVPlayerItem
before buffering- Set
AVPlayer.rate
for setting item
- Set
-
Observe
AVPlayer.status
andAVPlayerItem.status
- Don't begin playback until status is
.readyToPlay
- Check
error
property when status is.failed
- Rebuild AVFoundation objects if
.mediaServicesWereReset
- Rebuild AVFoundation objects if
- Don't begin playback until status is
-
Enable
AVPlayer.usesExternalPlaybackWhileExternalScreenIsActive
-
Configure
AVAudioSession
for.playback
- Use
.contentOverlayView
- Okay to use before setting player or player item
- Observe
.isReadyForDisplay
- Returns
true
when first frame is ready
- Returns
let token = pvc.observe(\.isReadyForDisplay, options: [.initil]) {
[weak self] observed, _ in
if observed.isReadyForDisplay {
// Hide any poster frame or placeholder
}
}
Continue playing when entering background. If you must pause, wail until in background
Restoring from picture-in-picture using AVPlayerViewControllerDelegate
func playerViewController(
_ playerViewController: AVPlayerViewController,
restoreUserInterfaceForPictureInPictureStopWithCompletionHandler
completionHandler: @escaping (Bool) -> Void) {
presentingViewController.present(playerViewController, animated: true) {
// Must invoke completionHandler
completionHandler(true)
}
}
Set the customOverlayViewController
property of the playerViewController
let customInteractiveVideoOverlay = UIViewController(nibName: "CustomInteractiveVideoOverlay", bundle: nil)
newPlayerViewController.customOverayViewController = customInteractiveVideoOverlay
- For live streams
- Swipe horizontally for next / previous channels
- Channel interstitial screen describes a channel with it loads
- Controls revealed by swipe-up? Use a custom overlay instead
- Use the delegate dismissal notifications rather than a
UITapGestureRecognizer
- Avoid toggling
showPlaybackControls
during presentation - Provide media content ratings
- Test with parental content restrictions enabled