For the next few chapters, we will be exploring some of the more common Apple TV user interface elements while building a Photo Gallery application. Most of the user interface elements will be familiar to you if you are an experienced iOS developer, but the way that the user interacts with them is somewhat different since you cannot walk up to your television and start tapping and swiping on the screen. (Not yet, anyway!)
Page View Controllers
The first user interface element that you will be exploring is the Page View Controller. The Page View Controller consists of a number of full-screen views that the user navigates through by swiping left or right between them. A common component of the Page View Controller is the Page Control, which is a series of horizontal white dots along the bottom of the views to indicate both the number of pages available as well as which page the user is currently viewing. An example of a Page View Controller containing a number of full-screen images is shown in Figure 4-1.
Figure 4-1. An example of a Page View Controller and its associated Page Control
From the Page Control near the bottom, you can see that there are five pages in this Page View Controller, and the user is currently looking at page number three.
For the Photo Gallery app, you are going to create an app consisting primarily of a Page View Controller that will present the user with a series of full-screen Image View pages. With each page containing a different photo, this will really take advantage of the full 1080p high-definition screen available to an Apple TV app.
Creating the Photo Gallery App
Figure 4-2. Creating a new Single View Application tvOS project
Figure 4-3. Creating the Photo Gallery Project
You should now be looking at your newly created Photo Gallery project, as shown in Figure 4-4.
Figure 4-4. The newly created Photo Gallery project
Now that you have created your project, you first have to do a little cleanup. Since this app will be built around a Page View Controller, you don’t need the default View Controller that was created with the project.
Figure 4-5. Removing the default View Controller
Figure 4-6. Selecting the View Controller scene
Now that the default View Controller has been removed, it is time to add the Page View Controller to the project.
Adding the Page View Controller
Figure 4-7. Adding a new Cocoa Touch Class to the project
Figure 4-8. Adding the new PageViewController class to the project
Now that you have added the PageViewController class, you need to add a Page Content View Controller to the project to handle displaying the pages of images from within the Page View Controller itself.
Adding Scenes to the Interface Builder Canvas
Now that you have added the PageViewController and PageContentViewController classes to the project, the next step is to add the associated scenes to the Main.storyboard file and associate them with your two new classes.
Figure 4-9. Adding a new Page View Controller to the Main.storyboard and setting it as the Initial View Controller
Note In tvOS, the scenes within a storyboard are large, each the size of a full 1080p television screen. This can make it difficult to navigate a storyboard containing multiple scenes. To remedy this, you can zoom in and out of a storyboard by pressing Command + and Command –, respectively.
Figure 4-10. Setting the Transition Style to Scroll
Figure 4-11. Setting the Page View Controller scene class to PageViewController
Now that you have added the Page View Controller scene to the Main.storyboard, you need to add an additional View Controller for the PageContentViewController class.
Figure 4-12. Setting the Page Content View Controller scene class and Storyboard ID to PageContentViewController
Now that you have the Page View Controller and Page Content View Controller scenes added to the Main.storyboard, you need to add the Image View to the Page Content View Controller to actually display the photos for our Photo Gallery.
Note Before adding the Image View to the Page Content View Controller scene, you will need to zoom back in again to 100% if you have previously zoomed out. The easiest way to do this is to hold down the Control and Command keys and press =. You can also right-click (or Control-click) anywhere on the empty blank space of the canvas and choose Zoom to 100%.
Figure 4-13. Adding an Image View to the Page Content View Controller scene
Since you are going to want the Image View to fill the entire screen, you will want to add some auto layout constraints to the Image View to pin it to the edges of the Page Content View Controller.
Figure 4-14. Adding constraints to the Image View
Now that the Image View fills the entire Page Content View Controller, the last thing you need to do is create an outlet for the Image View in the PageContentViewController.swift file.
Figure 4-15. Viewing the Page Content View Controller in the Assistant editor
Figure 4-16. Adding the imageView outlet to the PageContentViewController class
Now that the Image View is connected to the imageView outlet, the scene work in Interface Builder is complete. You can now re-select the Standard editor (by clicking the icon in the top-right corner of Xcode) and turn your attention to adding the required data model structures to the project for the Photo Gallery app.
Adding the Photo and Album Data Model Structures
Since the purpose of a Photo Gallery app is to display a collection of photos, you are going to want to represent each of those photos using a custom data structure in order to keep things as clean and organized as possible. You are also going to create an Album structure to store a collection of related Photos. The completed app will allow the user to browse through an album of photos using the Page View Controller you have already created. So let’s get started!
First, let’s start by creating a Model group for these new structures under the Photo Gallery group in the Project navigator.
Add the following code into the newly created Photo.swift file:
1 import Foundation
2
3 struct Photo {
4 var name: String = ""
5
6 init(name: String) {
7 self.name = name
8 }
9 }
This Swift Photo structure contains a single property, a String called name (Line 4), which will store the name of the photo associated with it.
Next, add another Swift File called Album.swift and add to it the code below:
1 import Foundation
2
3 struct Album {
4 var name: String = ""
5 var photos: [Photo] = []
6
7 init(name: String, photoNames: [String]) {
8 self.name = name
9 for photoName in photoNames {
10 self.photos += [Photo(name: photoName)]
11 }
12 }
13 }
The Album structure also contains a String name property (Line 4) as well as an array of Photo structures called photos (Line 5) that make up the contents of the album.
The Album structure also has a designated initializer (Line 7) that takes in the name of an album as well as an array of photo name Strings. The array of photo name Strings is then used to create the photos array of Photo structures when the Album is initialized (Lines 9-11).
Adding the Photo Image Files to the Asset Catalog
Now that you have created the Photo and Album data model structures, next you are going to add the actual photo image files to the project.
The best way to add images to an Xcode project is by adding them to an asset catalog. Asset catalogs simplify the organization and management of images in your app. When you created the Photo Gallery project, a default asset catalog named Assets.xcassets was created automatically.
Figure 4-17. The default Assets.xcassets asset catalog
To add the photo image files to the asset catalog, first download the image files, as discussed in the Introduction to this book. After they have been downloaded and unzipped, simply drag and drop the Animals folder into the asset catalog set list, as shown in Figure 4-18.
Figure 4-18. Adding the Animals folder of images to the asset catalog
Adding images to an asset catalog automatically copies the images to the project, so once they have been added, feel free to delete the original files you downloaded.
By default, when images are added to an asset catalog, they are added for All Universal devices, as shown in Figure 4-19. This means that a single image in an asset catalog can support multiple files at different resolutions (1x, 2x, 3x) for the various Apple devices available.
Figure 4-19. By default, images added to an asset catalog are added for All Universal devices
You don’t need to support multiple resolutions as you are developing an Apple TV application, so for each of the images in the Animals folder, check the TV OS Apple TV check box, uncheck the All Universal check box, and then drag the image from the Unassigned spot to the 1x Apple TV spot, as shown in Figure 4-20.
Figure 4-20. Reassigning all of the Universal images to Apple TV images in the asset catalog
Now that the photo image files have been added to the project, you can start filling in the details of the PageViewController and PageContentViewController classes. You are well on our way to finishing up this app!
Completing the Photo Gallery App
The only changes you have made to the PageContentViewController class up until this point was to add the UIImageView outlet earlier in the chapter. You still need to make a few more changes to complete the functionality of the Page Content View Controller.
After selecting the PageContentViewController.swift file from the Project navigator, add the following two lines to the beginning of the class definition:
1 var index: Int = 0
2 var photoName: String = ""
Since there will be multiple instantiations of the PageContentViewController class (one for each page in the Page View Controller), you will want each Page Content View Controller to keep track of both its page index (Line 1) and the name of its photo (Line 2). The Page View Controller will initialize these properties when it creates the Page Content View Controller.
Finally, add the following lines to the end of the viewDidLoad method:
1 if let image = UIImage(named: self.photoName) {
2 self.imageView.image = image
3 }
This initializes the Image View, loading in the photo represented by the photoName property that was set by the Page View Controller when it created the Page Content View Controller.
The final changes you need to make to complete the Photo Gallery app are within the main PageViewController class. Select the PageViewController.swift file in the Project navigator and have the PageViewController class adopt the UIPageViewControllerDataSource protocol by editing the first line of the class definition to match the following line of code:
1 class PageViewController: UIPageViewController, UIPageViewControllerDataSource {
Next, add the following properties at the beginning of the class definition:
1 var pageIndex: Int = 0
2 var album = Album(name: "Animals",
3 photoNames: ["Cows", "Dog",
4 "Horses", "Seagull", "Sheep"])
The pageIndex property (Line 1) keeps track of which page the Page View Controller is currently displaying, and the album property (Lines 2-4) is the Album structure that provides the Photos information to the Page Content View Controllers.
Next, add the following lines to the end of the viewDidLoad method:
1 self.dataSource = self
2 if let pageContentViewController =
3 self.pageContentViewController(self.pageIndex) {
4 self.setViewControllers([pageContentViewController],
5 direction: .Forward, animated: true, completion:
6 nil)
7 }
Setting itself as its own data source (Line 1) means that it has adopted the UIPageViewControllerDataSource protocol, and it will then be able to provide the data necessary to display the various Page Content View Controller pages. After setting the data source, you generate the initial Page Content View Controller (Lines 2-3) using the pageIndex property (which has been initialized to 0), and initialize the Page View Controller to display the new Page Content View Controller with a Forward navigation direction (Lines 4-6).
Now that you have identified the PageViewController as its own data source, you need to add the following methods required by the UIPageViewControllerDataSource protocol to the end of the PageViewController class definition:
1 func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
2 if let contentViewController = viewController as? PageContentViewController {
3 return self.pageContentViewController(contentViewController.index - 1)
4 }
5
6 return nil
7 }
8
9 func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
10 if let contentViewController = viewController as? PageContentViewController {
11 return self.pageContentViewController(contentViewController.index + 1)
12 }
13
14 return nil
15 }
When the user is swiping back and forth between pages, these two methods are called to provide the Page View Controller with the page that is before or after the current page, depending on whether the user has swiped backward or forward, accordingly. If there is no page before or after the current page (depending on which direction the user swiped), then the methods simply return nil.
Add the next two methods at the end of the class definition for the two UIPageViewControllerDataSource protocol methods, which provide the data needed to display the Page Control:
1 func presentationCountForPageViewController(pageViewController: UIPageViewController) -> Int {
2 return album.photos.count
3 }
4
5 func presentationIndexForPageViewController(pageViewController: UIPageViewController) -> Int {
6 return self.pageIndex
7 }
The first returns the total number of pages for the Page View Controller, which in this case is the number of Photos contained within the Album. The second returns the current page index so that the Page Control knows which dot should be selected.
The final method you will add to the end of the class definition is the one that returns the instantiated Page Content View Controllers for a specified page index:
1 func pageContentViewController(index: Int) -> PageContentViewController? {
2 if let contentViewController = self.storyboard?.instantiateViewControllerWithIdentifier("PageContentViewController") as? PageContentViewController where index >= 0 && index < album.photos.count {
3 self.pageIndex = index
4 contentViewController.index = index
5 contentViewController.photoName = self.album.photos[index].name
6 return contentViewController
7 }
8
9 return nil
10 }
If an invalid index is passed that is beyond the number of photos within the album, the method simply returns nil (Line 9). If the index is valid, then the pageIndex property is updated with the new index (Line 3) and a new Page Content View Controller is created (Line 2) and initialized (Lines 4-5) with the index and the photo name from the associated Photo in the Album before it is returned (Line 6).
That’s it! If you run the app by clicking the Build and run button in Xcode, it should run in the Apple TV simulator and display a full-screen image of a number of cows grazing, as shown in Figure 4-21.
Figure 4-21. The completed Photo Gallery app
Using the Apple TV Remote in the Simulator will allow you to swipe between the five different photos from the Animals album. Tapping on the left and right sides of the remote will allow you to scroll through the images as well.
Summary
In this chapter you created a Photo Gallery app to view multiple high-resolution photo image files using a Page View Controller. This has given you a solid starting point for learning more about the different User Interface controls available in tvOS.
In the next chapter we are going to expand on the Photo Gallery app by adding the ability for the user to choose from a list of multiple albums, and then browse the photos within them. We are also going add a custom static Top Shelf image to further showcase the contents of the app from the Home screen of the Apple TV.
Exercises