Finally Apple is allowing developers the ability to make our own Apple TV apps using the new tvOS SDK. tvOS is just about the same as developing for iOS but with a couple restrictions. No web views are allowed, tvOS does not contain all of the UI objects that are in iOS but it does have most of them, like buttons, alert views, table views and collection views which are probably the most common objects found in Apple TV apps currently. The other restriction is you can only use third-party frameworks that are built specifically for tvOS, you cannot reuse the same frameworks or libraries you use in your iOS or Watch OS apps.
In this tutorial we are going to build the most common type of tvOS app that is currently on the Apple TV platform, a video app. We’ll try to recreate a Netflix style layout and look at the classes and objects needed to make browsing and viewing TV shows and movies.
Project Setup
- Create a new project, File > New > Project
- Choose Application under tvOS and select “Single View Application”
- Name the Product “Video App”, and we’re going to develop it in Swift
Setting up the Menu
The main menu for most Apple TV apps is a tab bar controller, except for tvOS apps it’s at the top of the screen instead of the bottom like iOS apps. This menu will let the user select between the sections of the app, i.e. Movies or TV Shows.
- Click “Main.storyboard” from the project navigator.
- From the UI objects library in the bottom right of Xcode, find “Tab Bar Controller” and drag it onto the Storyboard. Use the pinch gesture on your trackpad to zoom out so you can see everything in the Storyboard.
- Select the Item 2 View Controller and name it “TV” and change the “Item 2” bar item to “TV”
- Do the same for the Item 1 Scene but change that to “Movies”
- Run the program and you’ll see that we have a standard tab bar with both Movies and TV. You can use your arrow keys to switch back and forth between view controllers.
Designing the Featured Movies Layout
Our layout is going to include one top row with the main featured movies or shows then a few rows after it which are individual movies and shows that the user can browse through, just like Netflix.
The secret to creating a tvOS app similar to the ones on the Apple TV now is to put all of your objects into a scroll view. As the user moves the focus down the page the scroll view will automatically move up or down so the user can see objects that are outside of the current visible area.
- Starting with the Movies Scene selected, uncheck “Under Top Bars” and “Under Bottom Bars” from the Attributes Inspector on the right. This will allow the Menu to hide smoothly when the user is at the top of the screen.
- Click on the view so we can customize the layout for our scrolling movie lists.
- From the UI objects library drag out a scroll view and make it the entire size of the view controller 1920 x 940.
- Next drag a UICollectionView from the UI objects library and place it at the top of the screen. This will be our main featured area. It’s size should be 1920×400.
- Set the Cell Size to 640 x 480 so we can fit 3 on the screen.
- Add your featured assets to the project. Right click on the Project Navigator and select “Add Files to …”, select the images you want to store locally.
- Back to the Storyboard, from the UI objects library drag a UIImageView into the Collection View Cell, it should be the entire width and height of the cell.
Custom Featured Collection View Cell
We need to create a custom collection view cell so we can tailor the layout of the cell to exactly how the design looks. We’ll call it FeaturedCollectionViewCell. Start with the large featured collection view cells.
- Right click in the Project Navigator, select “New File”, choose “tvOS > Source > Swift File”.
- Enter in the name of the custom class FeaturedCollectionViewCell and click “Create”.
Below is the code for the featured collection view class, it only contains 1 object, an UIImageView.
import UIKit class FeaturedCollectionViewCell: UICollectionViewCell { @IBOutlet weak var featuredImage: UIImageView! override init(frame: CGRect) { super.init(frame: frame) self.commonInit() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } private func commonInit() { // Initialization code self.layoutIfNeeded() self.layoutSubviews() self.setNeedsDisplay() } override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { if (self.focused) { self.featuredImage.adjustsImageWhenAncestorFocused = true } else { self.featuredImage.adjustsImageWhenAncestorFocused = false } } override func layoutSubviews() { super.layoutSubviews() } override func awakeFromNib() { super.awakeFromNib() } override func prepareForReuse() { super.prepareForReuse() } }
You’ll notice a new method you’ve never seen before:
override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) { if (self.focused) { self.featuredImage.adjustsImageWhenAncestorFocused = true } else { self.featuredImage.adjustsImageWhenAncestorFocused = false } }
Since tvOS doesn’t have touch capability like iOS or Watch OS this method tells the cell what to do when it is in focus. The adjustsImageWhenAncestorFocused property for the UIImageView will make the image expand and popout when it’s in focus for the user. This is the most common UI indicator for the user when browsing through the content.
Hook up the Featured Cell
Now that we have the custom class, we need to implement the UI objects for it within the Storyboard.
- Click “Main.storyboard” from the project navigator.
- Click the Cell under Collection View 1 and inside the Attributes Inspector change the Identifier to “FeaturedCell”. This will be our re-useable identifier when creating the cells.
- Enter the custom class “FeaturedCollectionViewCell” with the object so you can hookup the IBOutlet for the UIImageView we defined earlier.
- From the UI objects library in the bottom right of Xcode, find “Image View” and drag it onto the collection view cell. Then hookup the IBOutlet by dragging the featuredImage outlet to the FeaturedCell.
Movies View Controller
- Make a new Swift file for our Movies View Controller. Right Click on the Project Navigator and select “New File”, choose “tvOS > Source > Swift File”. Name it “MoviesViewController” and click the “Create” button.
- Enter the following code, this is everything required to get the scroll view and collection view to work with the images we have within the project.
import Foundation import UIKit class MoviesViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UIScrollViewDelegate { @IBOutlet var scrollView : UIScrollView! @IBOutlet var collectionView1 : UICollectionView! let reuseIdentifierFeatured = "FeaturedCell" override func viewDidLoad() { super.viewDidLoad() } override func viewDidLayoutSubviews() { self.scrollView!.contentSize = CGSizeMake(1920, 2200) } // Collection View Methods func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat { return 50 } func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat { return 50 } func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAtIndex section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: 0.0, left: 50.0, bottom: 0.0, right: 50.0) } func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { return 1 } func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { if (collectionView == self.collectionView1) { return 8 } return 0 } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { if (collectionView == self.collectionView1) { let cell : FeaturedCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(self.reuseIdentifierFeatured, forIndexPath: indexPath) as! FeaturedCollectionViewCell let imageFilename = "feature-\(indexPath.row).jpg" cell.featuredImage.image = UIImage(named: imageFilename) return cell } return UICollectionViewCell() } }
Note: As of Beta3 the scrollView contentSize should be entered within the viewDidLayoutSubviews() method since the view size is not yet set when viewDidLoad() is called
Hook up the Outlets
All of the code has been written in the Movie View Controller class, so we just need to hookup the outlet and delegates for the scroll view and collection view.
- Click “Main.storyboard” from the project navigator.
- Define the custom class for the Movies view controller.
- Click the scroll view and drag the outlet to the Movies
- Click the Collection View and drag the delegates and outlet to the Movies view controller so we can control it within the MoviesViewController.swift file.
Run It
If everything is linked up correctly and the code above all entered you should see the first collection view on the screen with the images displayed.
Scrolling Movies
If you would like to have categories that the user can browse through, you can make them by adding more collection views below the featured one that we just created. Each collection view will be unique with it’s own set of data.
Download the entire project at the end of this tutorial to see how I did it.
Playing Video
The last feature we need to make is allowing the user to watch video once one of the movies or TV shows has been selected.
- Make a new Swift file for our Player View Controller. Right Click on the Project Navigator and select “New File”, choose “tvOS > Source > Swift File”. Name it “PlayerViewController” and click the “Create” button.
- Add the following code to it:
import Foundation import UIKit import AVKit class PlayerViewController: AVPlayerViewController, AVPlayerViewControllerDelegate { override func viewDidLoad() { super.viewDidLoad() } func playVideo() { player = AVPlayer(URL: NSURL(string: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!) player?.play() } }
- Add the following collection view method to the the MoviesViewController.swift so when a cell is selected it’ll open up the PlayerViewController:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { let playerVC = PlayerViewController() playerVC.playVideo() [self.presentViewController(playerVC, animated: true, completion: nil)] }
- Click “Main.storyboard” from the project navigator.
- From the UI objects library in the bottom right of Xcode, find “AVKit Player View Controller” and drag it onto the Storyboard.
- Change the custom class name to “PlayerViewController”.
- Before we can run this, as of iOS 9, we need to allow our app to access external URLs, like the one in our PlayerViewController that links to the video we’re playing.
- Click the Info.plist from the Project Navigator and add the keys below:
Watch Video
Lastly lets test to see if our video player works. Run the app, scroll to a video cell and click the Select button on the new Apple TV remote (or Enter key in the simulator) to play the video. You should see the screen below.
There you have it, your own video app where users can browse through titles by category and select one to watch a video. Be sure to download the sample app below so you can see the full working app.
You can grab the full source code for this tutorial.
Note: Created using Xcode 7.1 GM (tvOS & Swift 2).