CHAPTER 6

image

Adding a Dynamic Top Shelf to the Photo Gallery App

The Top Shelf area of the Apple TV Home screen is a great place to provide more information about an app, as well as to showcase what an app has to offer to its users. At the end Chapter 5, you added a custom static Top Shelf image to the Photo Gallery app. In this chapter, you are going to customize the Top Shelf even further by adding a scrollable collection of dynamic thumbnail images for users to choose from, as shown in Figure 6-1.

9781484217146_Fig06-01.jpg

Figure 6-1. The dynamic Top Shelf of the Photo Gallery app

Users will be able to scroll through the collection of thumbnail images to see a preview of all of the albums within the app. Selecting any of the thumbnail images will open the Photo Gallery app, taking the user to the selected full-screen photo.

Application Extensions

You will add support for a dynamic Top Shelf by adding a new TV Services application extension that implements the TVTopShelfProvider protocol to the Photo Gallery app. App extensions are not apps themselves, but instead allow apps to provide additional functionality to the rest of the system. Apple Watch apps, custom keyboards, and Today widgets are some of the other examples of app extensions, specifically for iOS.

  1. To get started, launch Xcode and open the Photo Gallery project from Chapter 5.
  2. Next, select File image New image Target from the Xcode application menu.
  3. Select Application Extension under tvOS and choose TV Services Extension, as shown in Figure 6-2.

    9781484217146_Fig06-02.jpg

    Figure 6-2. Adding a TV Services Extension to the Photo Gallery app

  4. Click Next and enter Photo Gallery Extension for the Product Name, as shown in Figure 6-3.

    9781484217146_Fig06-03.jpg

    Figure 6-3. Adding the Photo Gallery Extension to the Photo Gallery app

  5. Click Finish and then Activate, if prompted. The Photo Gallery project should now include the Photo Gallery Extension target, as shown in Figure 6-4.

    9781484217146_Fig06-04.jpg

    Figure 6-4. The Photo Gallery Extension has been added to the Photo Gallery project

Adding Classes and Images to the Photo Gallery Extension

Since the app extension is a separate target from the main Photo Gallery app, you first need to add the classes and images that it needs to know about to generate the dynamic Top Shelf data.

  1. With the Photo Gallery project selected in the Project navigator, select the Photo Gallery Extension target and click the Build Phases tab, as shown in Figure 6-5.

    9781484217146_Fig06-05.jpg

    Figure 6-5. The Build Phases tab selected for the Photo Gallery Extension target

  2. Click the triangle next to the Compile Sources item to expand it, as shown in Figure 6-6.

    9781484217146_Fig06-06.jpg

    Figure 6-6. The Compile Sources Build Phase of the Photo Gallery Extension target

  3. Click the + button at the bottom of the Compile Sources list and add the Gallery.swift, Album.swift, and Photo.swift files, as shown in Figure 6-7.

    9781484217146_Fig06-07.jpg

    Figure 6-7. Adding the Gallery, Album, and Photo classes to the Photo Gallery Extension

Now that the Photo Gallery Extension knows what a Gallery, Album, and Photo are, you just need to add the actual image files to the target so that they can be used to generate the thumbnail images.

  1. To add the image files to the Photo Gallery Extension, first right-click (or Control-click) the Photo Gallery Extension group, select New Group, and name it Photos.
  2. Download and unzip the associated project files for this chapter, and drag and drop the Animals, Cities, and Landscapes folders onto the new Photos group in Xcode. When prompted, make sure the Copy items if needed check box is checked, the Create groups radio button is selected, and the Photo Gallery Extension target check box is checked before clicking the Finish button, as shown in Figure 6-8.

    9781484217146_Fig06-08.jpg

    Figure 6-8. Adding the image files to the Photo Gallery Extension target

Image Note  In case you were wondering why the image files were not added to an Asset Catalog in this instance, it is because the dynamic Top Shelf thumbnail images need to be initialized using an image file URL. Image file URLs are not available when images are packaged up in an asset catalog, so that is why you instead add them to the Photo Gallery Extension target as general bundle resources.

Implementing the TVTopShelfProvider Protocol

Now that the Photo Gallery Extension target contains the necessary classes and images needed for the dynamic Top Shelf data, it is time to generate and return that data by implementing the TVTopShelfProvider protocol in the ServiceProvider.swift file.

Select the ServiceProvider.swift file from the Project navigator and replace the default topShelfItems computed property definition with the code below:

 1      var topShelfItems: [TVContentItem] {
 2          let gallery = Gallery()
 3
 4          var albums: [TVContentItem] = []
 5
 6          // create a TVContentItem for each album in the gallery
 7          for albumIndex in 0..<gallery.albums.count {
 8              let album = gallery.albums[albumIndex]
 9
10              var photos: [TVContentItem] = []
11
12              // create a TVContentItem for each photo in the album
13              for photoIndex in 0..<album.photos.count {
14                  let photo = album.photos[photoIndex]
15
16                  guard let photoIdentifier = TVContentIdentifier(identifier: photo.name, container: nil) else { return [] }
17                  guard let photoItem = TVContentItem(contentIdentifier: photoIdentifier) else { return [] }
18
19                  photoItem.title = photo.name
20                  photoItem.imageURL = NSBundle.mainBundle().URLForResource(photo.name, withExtension: ".jpg")
21                  photoItem.displayURL = NSURL(string: "photogallery:viewTopShelfItem?album=(albumIndex)&photo=(photoIndex)")
22
23                  photos.append(photoItem)
24              }
25
26              guard let albumIdentifier = TVContentIdentifier(identifier: album.name, container: nil) else { return [] }
27              guard let albumItem = TVContentItem(contentIdentifier: albumIdentifier) else { return [] }
28
29              albumItem.title = album.name
30              albumItem.topShelfItems = photos
31
32              albums.append(albumItem)
33          }
34
35          return albums
36      }

The topShelfItems computed property is now going to return an array of TVContentItems, one for each album. Each album TVContentItem within the returned array is going to contain another array of TVContentItems, one for each photo within that album.

At the beginning of the topShelfItems computed property code block, you first create an instance of the Gallery class (Line 2) so that you can reference all of the album and photo data and information. Next, you loop over all of the albums in the gallery (Line 7) to add the associated TVContentItems to the albums array. Then, within the photos for loop, you will loop over all of the photos within that album (Line 13) to add the associated TVContentItems to the photos array.

Within the photo for loop, you first create a TVContentIdentifier using the name of the current photo (Lines 14-16), and then create the actual TVContentItem using that TVContentIdentifier (Line 17). You then set the title of the photo item to be the name of the photo (Line 19), so that it will be displayed underneath the thumbnail image when displayed in the Top Shelf, as shown in Figure 6-9.

9781484217146_Fig06-09.jpg

Figure 6-9. The title of the first TVContentItem in the Animals album is Cows

After setting the title, you then set the imageURL (Line 20) to be the location of the associated image file that you copied to the Photo Gallery Extension earlier in the chapter. Finally, you set the displayURL (Line 21) to a specially formatted string that will be passed to the Photo Gallery application to identify which image thumbnail was selected from the Top Shelf. The displayURL string contains the photogallery scheme (which we will discuss later in the chapter) as well as the album and photo index values associated with the photo.

Once those three properties have been set for the current photo, the TVContentItem is added to the photos array at the end of the loop (Line 23).

After the photo loop is complete, the photos array contains all of the photo TVContentItems for the current album. Next, you create a TVContentIdentifier and TVContentItem for the album, just like you did for each photo (Lines 26-27). Then, you set the title of the TVContentItem to the album name (Line 29) so it will appear above each collection of image thumbnails, as shown in Figure 6-9.

After setting the topShelfItems property to be the photos array (Line 30), you add the completed album TVContentItem to the albums array at the end of the album loop (Line 32). Once you have added all of the albums to the albums array, the array is returned (Line 35).

Phew! That was quite a lot of code, but you got through it! Now, if you attempt to build and run the app extension, you will be presented with the dialog window shown in Figure 6-10. Because app extensions are not apps that can be run independently, you need to select Top Shelf from the list to see your changes reflected in the Top Shelf of your Apple TV.

9781484217146_Fig06-10.jpg

Figure 6-10. Selecting which app to run with the Photo Gallery Extension

After clicking Run, you will see the static Top Shelf image you added in Chapter 5 replaced with the dynamic thumbnails for all of the albums and photos contained within the gallery. Swiping up on the Apple TV remote when the Photo Gallery app is selected allows you to focus on the Top Shelf items and swipe back and forth between all of the available photos.

Launching the Photo Gallery App from a Top Shelf Thumbnail Image

Browsing through all of the thumbnails is great, but what we really want to do is click on one of those thumbnails to view the selected image within the Photo Gallery app. To allow the Photo Gallery Extension to do this, you are first going to have to make some changes to the main Photo Gallery app.

  1. In the Project navigator, select the Info.plist file that is within the Photo Gallery group, not the one within the Photo Gallery Extension group, as shown in Figure 6-11.

    9781484217146_Fig06-11.jpg

    Figure 6-11. Selecting the Info.plist of the Photo Gallery app

  2. Once the Info.plist has been selected, hover the mouse pointer over the last item in the list, click the + button that appears to add a new key to it, and choose URL types from the list, as shown in Figure 6-12.

    9781484217146_Fig06-12.jpg

    Figure 6-12. Adding the URL types item to the Info.plist

  3. After adding the URL types key to the Info.plist, click the small gray triangle next to it to expand it, revealing the Item 0 subkey.
  4. Then click the small gray triangle next to the Item 0 key to reveal the URL identifier subkey underneath it.
  5. Click in the Value column of the URL identifier item and change the value to Photo Gallery URL, as shown in Figure 6-13.

    9781484217146_Fig06-13.jpg

    Figure 6-13. Changing the URL identifier to Photo Gallery URL

  6. Next, click the + button from within the URL identifier item and select URL Schemes from the list, as shown in Figure 6-14.

    9781484217146_Fig06-14.jpg

    Figure 6-14. Adding the URL Schemes subkey to the Info.plist

  7. Click the gray triangle next to the URL Schemes key to expand it to reveal its Item 0 subkey. Change the value of Item 0 to be photogallery, as shown in Figure 6-15.

    9781484217146_Fig06-15.jpg

    Figure 6-15. Adding the photogallery URL Scheme to the Info.plist

Adding the photogallery URL Scheme to the Info.plist registers that scheme (as mentioned earlier in the chapter) with the system. Now, whenever any URL that begins with photogallery is opened on the Apple TV, it will be opened by the Photo Gallery app.

Build and run the app extension and select one of the thumbnails from the Top Shelf. The Photo Gallery app is now launched, but the image that was selected is not yet being displayed. To accomplish this, you first need to tell the Photo Gallery application how to handle the URL information that is passed to the app when it is opened from a Top Shelf thumbnail image.

Handling URLs

When an Apple TV app is launched from a matching URL scheme, the URL is passed to a UIApplicationDelegate protocol method called openURL. Open the AppDelegate.swift file from the Project navigator and add the follow code to the end of the AppDelegate class declaration:

 1      func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
 2          var albumIndex: Int?
 3          var photoIndex: Int?
 4
 5          // extract the album and photo index from the url
 6          guard let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: false) else { return true }
 7          guard let queryItems = components.queryItems else { return true }
 8          for queryItem in queryItems {
 9              if let valueString = queryItem.value, value = Int(valueString) {
10                  if queryItem.name == "album" {
11                      albumIndex = value
12                  }
13                  else if queryItem.name == "photo" {
14                      photoIndex = value
15                  }
16              }
17          }
18
19          // if the album and photo index values have been set, view that photo
20          if albumIndex != nil && photoIndex != nil {
21              // pass the album and photo index values to the TableViewController
22              if let tableViewController = window?.rootViewController as? TableViewController {
23                  tableViewController.viewSelectedTopShelfPhoto(photoIndex!, inAlbum: albumIndex!)
24              }
25          }
26
27          return true
28      }

In this method, you first use the URL passed in from the Top Shelf TVContentItem to create an NSURLComponents object (Line 6) in order to extract the albumIndex and photoIndex values from the URL string (Lines 8-17). Then, if both of those values have been set (Line 20), you find the TableViewController object from the main app window (Line 22) and pass it to its viewSelectedTopShelfPhoto method (Line 23).

Completing the Photo Gallery App

Now you need to make the appropriate changes to the TableViewController class to handle this new method being called from the App Delegate. Open the TableViewController.swift file and add the following line of code to the top of the class declaration, beneath the gallery property declaration:

1       var selectedTopShelfItem: (albumIndex: Int?, photoIndex: Int?) = (nil, nil)

This defines a tuple property containing two integers, one for the album index and one for the photo index. Initially both of the index values are nil, indicating that a Top Shelf item has not been selected.

Next, define the viewSelectedTopShelfPhoto method by adding the following code to the end of the class declaration:

 1      func viewSelectedTopShelfPhoto(photo: Int, inAlbum album: Int) {
 2          // save the selected top shelf album photo index values
 3          self.selectedTopShelfItem = (album, photo)
 4
 5          // if I am not the presented view controller, pop back
 6          if let presentedViewController = self.presentedViewController {
 7              presentedViewController.dismissViewControllerAnimated(false, completion: nil)
 8          }
 9          else {
10              self.checkSelectedTopShelfItem()
11          }
12      }

In this method, you first store the album and photo index values within the new selectedTopShelfItem tuple property (Line 3). Then, you check to see what the current state of the app’s view controller hierarchy is. If a user has previously left the app viewing another full-screen image (Line 6), then you would want to dismiss the presented PageViewController object (Line 7) before viewing the newly selected photo. If there is no presentedViewController set (Line 9), then that means there is no PageViewController object to dismiss, so the user can continue to view the selected photo (Line 10).

Next, add the following code to the end of the class declaration to check whether a Top Shelf item has been selected, and if so, performing the appropriate action:

1      func checkSelectedTopShelfItem() {
2          if let albumIndex = self.selectedTopShelfItem.albumIndex {
3              self.tableView.selectRowAtIndexPath(NSIndexPath(forRow: albumIndex, inSection: 0), animated: false, scrollPosition: .None)
4              self.performSegueWithIdentifier("SelectAlbumSegue", sender: nil)
5          }
6      }

In this method, if the album index within the selectedTopShelfItem is set (Line 2), you can then select that row in the table view (Line 3) and manually perform the SelectAlbumSegue that is used when a user clicks one of the albums from the list to browse the photos within it (Line 4).

When a user selects a Top Shelf image and the app is opened, depending on its previous state, a PageViewController object may need to be dismissed. If that is the case, the app needs to be notified when that process is complete so that it can continue to view the selected Top Shelf item photo. The easiest way to do this is to override the Table View Controller’s viewDidAppear method by adding the following code to the TableViewController.swift file after the viewDidLoad method declaration:

1       override func viewDidAppear(animated: Bool) {
2           super.viewDidAppear(animated)
3           self.checkSelectedTopShelfItem()
4       }

Now, whenever the Table View Controller appears, the selectedTopShelfItem is checked (Line 3).

The final change you have to make is to pass the selected photo index to the PageViewController object from within the prepareForSegue method of the Table View Controller. Add the following changes to the TableViewController.swift file so that the prepareForSegue looks like this:

 1      override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
 2          if segue.identifier == "SelectAlbumSegue" {
 3              if let pageViewController = segue.destinationViewController as? PageViewController, row = self.tableView.indexPathForSelectedRow?.row {
 4                  pageViewController.album = self.gallery.albums[row]
 5
 6                  // if there is a selected photo index set it as well and then reset it
 7                  if let photoIndex = self.selectedTopShelfItem.photoIndex {
 8                      pageViewController.pageIndex = photoIndex
 9                      self.selectedTopShelfItem = (nil, nil)
10                  }
11              }
12          }
13      }

Now, if the segue matches the SelectAlbumSegue identifier (Line 2), after a Page View Controller is created (Line 3) and its album has been initialized (Line 4), its pageIndex is set to the photoIndex of the selectedTopShelfItem (Lines 7-8) so that the appropriate image will be selected once the app is launched. Finally, the selectedTopShelfItem is reset back to its uninitialized state (Line 9).

Now, when you select a thumbnail image from the Top Shelf, the Photo Gallery app is launched and the appropriate album is displayed with the appropriate photo already selected.

Summary

Congratulations! Over the course of the past few chapters, you have created a Photo Gallery app that enables users to select and view photos from within a number of different photo albums, in addition to viewing those photos when selecting them from the Top Shelf area of the Apple TV Home screen.

You have learned about using Page View Controllers, Table View Controllers, and Application Extensions on tvOS, all using Swift. The knowledge you have gained throughout these chapters will provide you with a great foundation for developing other apps in the future using these aspects of tvOS development.

In the next chapter, we will explore how to store app information on the Apple TV itself, as well as how to store and sync data to the cloud.

Exercises

  1. By default, the imageShape property of a TVContentItem is Square for the default Sectioned TVTopShelfContentStyle. The other options available are Poster and HDTV. Make the appropriate changes to the Photo Gallery Extension to utilize these different styles to see which style you like best.
  2. You can mix and match the TVTopShelfContentStyle values throughout the TVContentItems displayed in the Top Shelf. You currently have three albums, and there are three styles available. Make the appropriate changes to the Photo Gallery Extension to use a different TVTopShelfContentStyle for each album, or perhaps choose a random style for each and every photo to really give your app a unique look!
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset