Running videos live on an app can be tricky when accommodating screen size transitions. This is because a video player is normally given a fixed width and height, preventing it from adapting to changes in screen sizes. When we built the CBS Sports AppleTV app, we wanted users to be able to smoothly transition between small and full screens while still including player controls. Our solution? Build a resizable avplayer
This post will walk you through the steps to build a resizable video player, so you can build one yourself.
Let’s begin.
To play a video, you need an AVPlayer and an AVAsset. The simplest way to display a video is in an AVPlayerLayer, which you can initialize with your player and an asset video or stream:
let avAsset = AVAsset(url: NSURL(string: “”)) var avPlayer = AVPlayer(asset: avAsset) var videoLayer = AVPlayerLayer(player: avPlayer) videoLayer.frame = CGRectMake(515,0,1405,790) videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill videoChannelView.smallVideoView.layer.addSublayer(videoLayer) videoLayer.masksToBounds = true
The AVPlayerLayer is resizable. All you need to do is set the layer’s frame and it will animate to the new size. The small player is in the top right corner in Figure 1.
func launchFullScreenVideo() { videoLayer!.frame = CGRectMake(0,0,1920,1080) } func dismissFullScreenVideo() { videoLayer!.frame = CGRectMake(515,0,1405,790) }
Figure 1. AVPlayerLayer displaying the video behind some UIViews OK. Now you have a resizable video player. In order to display the video controls on the Apple TV, you will also need an AVPlayerViewController. Initialize it with the AVPlayer, and put the video in the AVPlayerViewController’s view. Your video should be playing in both the AVPlayerLayer and the AVPlayerViewController’s view.
You need to use the AVPlayerLayer to resize the video and the AVPlayerViewController to display the video with native controls. Remember: you can’t use both at the same time. If you try to resize the AVPlayerViewController’s view it won’t animate correctly, and if you only display the layer then you can’t use the controls.
The solution? Use two views: one should be a full screen view and the other should be within the view hierarchy that contains the AVPlayerLayer. The smallVideoView’s frame is only the size of the smaller player (even though the playerLayer scales itself to 1920×1080) so it fits nicely into the focus engine above the thumbnails and to the right of the menu.
Since the full screen view is in front, it needs to disable user interaction in order for the rest of the app to process events and receive focus. It will need to allow user interaction when you go to full screen mode. Our application’s view controller’s view is a VideoChannelView subclass of UIView
Instructions:
class VideoChannelView: UIView { lazy var smallVideoView : UIView = { let v = UIView() v.frame = UIScreen.mainScreen().bounds return v }() lazy var fullscreenVideoView : UIView = { let v = UIView() v.frame = UIScreen.mainScreen().bounds v.userInteractionEnabled = false return v }() func setupView() { < do some setup here> addSubview(smallVideoView) < do some more setup here, add more views > addSubview(fullscreenVideoView) } let videoPlayerViewController = AVPlayerViewController(player: avPlayer) func launchFullScreenVideo() { videoLayer!.frame = CGRectMake(0,0,1920,1080) performSelector(#selector(VideoChannelViewController.switchToFullscreen), withObject: nil, afterDelay: 0.225) } func switchToFullscreen() { videoChannelView.fullscreenVideoView.userInteractionEnabled = true videoChannelView.fullscreenVideoView.addSubview(videoPlayerViewController.view) videoPlayerViewController.view.hidden = true performSelector(#selector(VideoChannelViewController.showFullscreen), withObject: nil, afterDelay: 0.05) } func showFullscreen() { videoPlayerViewController.view.hidden = false } func dismissFullScreenVideo() { videoPlayerViewController.view.removeFromSuperview() videoLayer.frame = CGRectMake(515,0,1405,790) }
Figure 2.
AVPlayerViewController displaying the video in full screen Almost done, but you now have to wait until the animation of the playerLayer to full screen size is finished. You can try switching to the AVPlayerViewController’s view, but this can cause a slight hiccup in playback.
The solution? Add the view but keep it hidden for a fraction of a second. The transition should be perfectly smooth! Here’s a video to put all of this together. Figure 3. Video capture of the app showing the transitions between small and full screen/