Navigation Controllers and Table Views - Beginning iPhone Development with Swift: Exploring the iOS SDK (2014)

Beginning iPhone Development with Swift: Exploring the iOS SDK (2014)

Chapter 9. Navigation Controllers and Table Views

In the previous chapter, you mastered the basics of working with table views. In this chapter, you’ll get a whole lot more practice because we’re going to explore navigation controllers.

Table views and navigation controllers work hand in hand. Strictly speaking, a navigation controller doesn’t need a table view to do its thing. As a practical matter, however, when you implement a navigation controller, you almost always implement at least one table (and usually several) because the strength of the navigation controller lies in the ease with which it handles complex hierarchical data. On the iPhone’s small screen, hierarchical data is best presented using a succession of table views.

In this chapter, we’re going to build an application progressively, just as we did with the Pickers application back in Chapter 7. We’ll get the navigation controller and the root view controller working, and then we’ll start adding more controllers and layers to the hierarchy. Each view controller we create will reinforce some aspect of table use or configuration:

· How to drill down from table views into child table views

· How to drill down from table views into content views, where detailed data can be viewed and even edited

· How to use multiple sections within a table view

· How to use edit mode to allow rows to be deleted from a table view

· How to use edit mode to let the user reorder rows within a table view

That’s a lot, isn’t it? Well, let’s get started with an introduction to navigation controllers.

Navigation Controller Basics

The main tool you’ll use to build hierarchical applications is UINavigationController. UINavigationController is similar to UITabBarController in that it manages, and swaps in and out, multiple content views. The main difference between the two is thatUINavigationController is implemented as a stack, which makes it well suited to working with hierarchies.

Do you already know everything there is to know about stacks? If so, scan through the following subsection (or skip it altogether), and we’ll meet you at the beginning of the next subsection, “A Stack of Controllers.” If you’re new to stacks, continue reading. Fortunately, stacks are a pretty easy concept to grasp.

Stacky Goodness

A stack is a commonly used data structure that works on the principle of “last in, first out.” Believe it or not, a Pez dispenser is a great example of a stack. Ever try to load one? According to the little instruction sheet that comes with each and every Pez dispenser, there are a few easy steps. First, unwrap the pack of Pez candy. Second, open the dispenser by tipping its head straight back. Third, grab the stack (notice the clever way we inserted the word “stack” in there!) of candy, holding it firmly between your pointer finger and thumb, and insert the column into the open dispenser. Fourth, pick up all the little pieces of candy that flew all over the place because these instructions just never work.

OK, so far this example has not been particularly useful. But what happens next is. As you pick up the pieces and jam them, one at a time, into the dispenser, you are working with a stack. Remember that we said a stack was last in, first out? That also means first in, last out. The first piece of Pez you push into the dispenser will be the last piece that pops out. The last piece of Pez you push in will be the first piece you pop out. A computer stack follows the same rules:

· When you add an object to a stack, it’s called a push. You push an object onto the stack.

· The first object you push onto the stack is called the base of the stack.

· The last object you pushed onto the stack is called the top of the stack (at least until it is replaced by the next object you push onto the stack).

· When you remove an object from the stack, it’s called a pop. When you pop an object off the stack, it’s always the last one you pushed onto the stack. Conversely, the first object you push onto the stack will always be the last one you pop off the stack.

A Stack of Controllers

A navigation controller maintains a stack of view controllers. When you design your navigation controller, you’ll need to specify the very first view the user sees. As we’ve discussed in previous chapters, that view’s controller is called the root view controller, or just root controller, and is the base of the navigation controller’s stack of view controllers. As the user selects the next view to display, a new view controller is pushed onto the stack, and the view it controls appears. We refer to these new view controllers as subcontrollers. As you’ll see, this chapter’s application, Fonts, is made up of a navigation controller and several subcontrollers.

Take a look at Figure 9-1. Notice the title centered in the navigation bar and the back button on the left side of the navigation bar. The title of the navigation bar is populated with the title property of the top view controller in the navigation controller’s stack, and the title of the back button is populated with the title of the previous view controller. The back button acts similar to a web browser’s back button. When the user taps that button, the current view controller is popped off the stack, and the previous view becomes the current view.

image

Figure 9-1. The Settings application uses a navigation controller. The back button at the upper left pops the current view controller off the stack, returning you to the previous level of the hierarchy. The title of the current content view controller is also displayed

We love this design pattern. It allows us to build complex hierarchical applications iteratively. We don’t need to know the entire hierarchy to get things up and running. Each controller only needs to know about its child controllers, so it can push the appropriate new controller object onto the stack when the user makes a selection. You can build up a large application from many small pieces this way, which is exactly what we’re going to do in this chapter.

The navigation controller is really the heart and soul of many iPhone apps; however, when it comes to iPad apps, the navigation controller plays a more marginal role. A typical example of this is the Mail app, which features a hierarchical navigation controller to let users navigate among all their mail servers, folders, and messages. In the iPad version of Mail, the navigation controller never fills the screen, but appears either as a sidebar or a temporary view covering part of the main view. We’ll dig into that usage a little later, in Chapter 11.

Fonts: A Simple Font Browser

The application we’re about to build will show you how to do most of the common tasks associated with displaying a hierarchy of data. When the application launches, you’ll be presented with a list of all the font families that are included with iOS, as shown in Figure 9-2. A font family is a group of closely related fonts, or fonts that are stylistic variations on one another. For example, Helvetica, Helvetica-Bold, Helvetic-Oblique, and other variations are all included in the Helvetica font family.

image

Figure 9-2. This chapter application’s root view controller. Note the accessory icons on the right side of the view. This particular type of accessory icon is called a disclosure indicator. It tells the user that touching that row drills down to another table view

Selecting any row in this top-level view will push a view controller onto the navigation controller’s stack. The icons on the right side of each row are called accessory icons. This particular accessory icon (the gray arrow) is called a disclosure indicator, and its presence lets the user know that touching that row drills down to another table view.

Meet the Subcontrollers

Before we start building the Fonts application, let’s take a quick look at each of the views displayed by our subcontrollers.

The Font List Controller

Touching any row of the table shown in Figure 9-2 will bring up the child view shown in Figure 9-3.

image

Figure 9-3. The first of the Fonts application’s subcontrollers implements a table in which each row contains a detail disclosure button

The accessory icon to the right of each row in Figure 9-3 is a bit different. This accessory is known as a detail disclosure button. Unlike the disclosure indicator, the detail disclosure button is not just an icon—it’s a control that the user can tap. This means that you can have two different options available for a given row: one action is triggered when the user selects the row, and another action is triggered when the user taps the button. Tapping the small info button within this accessory should allow the user to view, and perhaps edit, more detailed information about the current row. Meanwhile, the presence of the right-pointing arrow should indicate to the user that there is some deeper navigation to be found by tapping elsewhere in the row.

The Font Sizes View Controller

Touching any row of the table shown in Figure 9-3 will bring up the child view shown in Figure 9-4.

image

Figure 9-4. Located one layer deeper than the Font List View Controller, the Font Sizes View Controller shows multiple sizes of the chosen font, one per row

Here’s a recap of when to use disclosure indicators and detail disclosure buttons:

· If you want to offer a single choice for a row tap, don’t use an accessory icon if a row tap will only lead to a more detailed view of that row.

· Mark the row with a disclosure indicator (right-pointing arrow) if a row tap will lead to a new view listing more items (not a detail view).

· If you want to offer two choices for a row, mark the row with either a detail disclosure indicator or a detail button. This allows the user to tap the row for a new view or the disclosure button for more details.

The Font Info View Controller

Our final application subcontroller—the only one that is not a table view—is shown in Figure 9-5. This is the view that appears when you tap on the info icon for any row in the Font List View Controller shown in Figure 9-2.

image

Figure 9-5. The final view controller in the Fonts application allows you to view the chosen font at any size you want

This view lets the user drag a slider to adjust the size of the displayed font. It also includes a switch that lets the user specify whether this font should be listed among the user’s favorites. If any fonts are set as favorites, they’ll appear within a separate group in the root view controller.

The Fonts Application’s Skeleton

Xcode offers a perfectly good template for creating navigation-based applications, and you will likely use it much of the time when you need to create hierarchical applications. However, we’re not going to use that template today. Instead, we’ll construct our navigation-based application from the ground up, so we get a feel for how everything fits together. We’ll also walk through it one piece at a time, so it should be easy to keep up.

In Xcode, press imageimageN to create a new project. Select Single View Application from the iOS template list, and then click Next to continue. Set Fonts as the Product Name, Swift as the Language, and select Universal for Devices. Make sure that Use Core Data is not checked, click Next, and choose the location to save your project.

Setting Up the Navigation Controller

We now need to create the basic navigation structure for our application. At the core of this will be a UINavigationController, which manages the stack of view controllers that a user can navigate between, and a UITableViewController that shows the top-level list of rows we’re going to display. As it turns out, Interface Builder makes this remarkably easy to do.

Select Main.storyboard. The template has created a basic view controller for us, but we need to use a UINavigationController instead, so select the view controller in either the editor area or the Document Outline and delete it to leave the storyboard empty. Now use the Object Library to search for UINavigationController and drag an instance into the editing area. You’ll see that you actually get two scenes instead of one, similar to what you saw when creating a tab view controller in Chapter 7. On the left is the UINavigationController itself. Select this controller, open the Attributes Inspector, and check Is Initial View Controller in the View Controller section to make this the controller that appears when the application is launched.

The UINavigationController has a connection wired to the second scene, which contains a UITableViewController. You’ll see that the table has the title Root View Controller. Click that title, open the Attributes Inspector, and then set the title to Fonts.

It’s worth taking a moment to think about this. What exactly do we get by configuring our application to load the initial scene from this storyboard? First, we get the view created by the navigation controller, a composite view that contains a combination of two things: the navigation bar at the top of the screen (which usually contains some sort of title and often a back button of some kind on the left) and the content of whatever the navigation controller’s current view controller wants to display. In our case, the lower part of the display will be filled with the table view that was created alongside the navigation controller.

You’ll learn more about how to control what the navigation controller shows in the navigation bar as we go forward. You’ll also gain an understanding of how the navigation controller shifts focus from one subordinate view controller to another. For now, you’ve laid enough groundwork that you can start defining what your custom view controllers are going to do.

At this point, the application skeleton is essentially complete. You’ll see a warning about setting a reuse identifier for a prototype table cell, but we can ignore that for now. Save all your files, and then build and run the app. If all is well, the application should launch, and a navigation bar with the title Fonts should appear. You haven’t given the table view any information about what to show yet, so no rows will display at this point (see Figure 9-6).

image

Figure 9-6. The application skeleton in action

Keeping Track of Favorites

At several points in this application, we’re going to let the user maintain a list of favorite fonts by letting them add chosen fonts, view a whole list of already-chosen favorites, and remove fonts from the list. In order to manage this list in a consistent way, we’re going to make a new class that will hang onto an array of favorites and store them in the user’s preferences settings for this application. You’ll learn a lot more about user preferences in Chapter 12, but here we’ll just touch on some basics.

Start by creating a new class. Select the Fonts folder in the Project Navigator and press imageN to bring up the new file assistant. Select Swift File from the iOS Source section and then click Next. On the following screen, name the new file FavoritesList.swift and press Create. Select the new file in the Project Navigator and add the following code shown in bold:

import Foundation
import UIKit

class FavoritesList {
class var sharedFavoriteList : FavoritesList {
struct Singleton {
static let instance = FavoritesList()
}
return Singleton.instance;
}

private(set) var favorites:[String]

init() {
let defaults = NSUserDefaults.standardUserDefaults()
let storedFavorites = defaults.objectForKey("favorites") as? [String]
favorites = storedFavorites != nil ? storedFavorites! : []
}

func addFavorite(fontName: String) {
if (!contains(favorites, fontName)) {
favorites.append(fontName)
saveFavorites()
}
}

func removeFavorite(fontName: String) {
if let index = find(favorites, fontName) {
favorites.removeAtIndex(index)
saveFavorites()
}
}

private func saveFavorites() {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(favorites, forKey: "favorites")
defaults.synchronize()
}
}

In the preceding snippet, we declared the API for our new class. For starters, we declared a class property called sharedFavoritesList that returns an instance of this class. No matter how many times this method is called, the same instance will always be returned. The idea is thatFavoritesList should work as a singleton—instead of using multiple instances, we’ll just use one instance throughout the application.

The implementation of this property is a little convoluted, because Swift does not support class properties unless they are of the computed type. Ideally, we would have created a class variable and initialized it with an instance of the class when it was first read, but we can’t do that (at least not yet—maybe support for class properties will be added at some point). Only structures support class properties (referred to in structures as static properties), so we created a structure called Singleton that contains the static property that we need and initialized it. Then, the implementation of the sharedFavoritesList property just returns the value of the structure’s instance property. Fortunately for us, Swift initializes static properties of structures lazily and in a thread-safe way, so the shared instance of the FavoritesList class won’t be created until thesharedFavoritesList property is read for the first time, and if multiple threads happened to try to read it at the same time, only one instance would actually be created (of course, that’s not going to happen in this single-threaded example).

Next, we declared a property to hold the names of our favorite fonts. Pay close attention to the definition of this array:

private(set) var favorites:[String]

The private(set) qualifier means that the array can be read by code outside the class, but only code in the class implementation can modify it. That’s exactly what we want, because we need users of our class to be able to read the favorites list:

let favorites = FavoritesList.sharedFavoriteList.favorites // Read-access is OK

But we don’t want either of these to be allowed:

FavoritesList.sharedFavoriteList.favorites = [] // Not allowed
FavoritesList.sharedFavoriteList.favorites.append("Comic Sans MS") // Not allowed

The class initializer is responsible for setting the initial content of the favorites array:

init() {
let defaults = NSUserDefaults.standardUserDefaults()
let storedFavorites = defaults.objectForKey("favorites") as? [String]
favorites = storedFavorites != nil ? storedFavorites! : []
}

As you’ll see shortly, any time we add something to or remove something from this array, we save its contents to the application’s user defaults (which we’ll discuss in detail in Chapter 12) so that the content of the list is preserved over application restarts. In the initializer, we check whether we have a stored favorites list, and if so, we use it to initialize the favorites property. If not, we simply make it empty.

The remaining three methods deal with adding to and removing from the favorites array. The implementations should be self-explanatory. Note that the first two methods both call saveFavorites(), which saves the updated value to the user defaults under the same key (“favorites”) as the initializer uses to read it. You’ll learn more about how this works in Chapter 12; but for now, it’s enough to know that the NSUserDefaults object that we use here acts like a sort of persistent dictionary, and anything that we put in there will be available the next time we ask for it, even if the application has been stopped and restarted.

Creating the Root View Controller

Now we’re ready to start working on our first view controller. In the previous chapter, we used simple arrays of strings to populate our table rows. We’re going to do something similar here, but this time we’ll use the UIFont class to get a list of font families, and then use the names of those font families to populate each row. We’ll also use the fonts themselves to display the font names, so that each row will contain a small preview of what the font family contains.

It’s time to create the first controller class for this application. The template created a view controller for us, but its name—ViewController—isn’t very useful, because there are going to be several view controllers in this application. So first select ViewController.swift in the Project Navigator and press Delete to delete it and move it to the trash. Next, select the Fonts folder in the Project Navigator and press imageN to bring up the new file assistant. Select Cocoa Touch Class from the iOS Source section and then click Next. On the following screen, name the new classRootViewController and enter UITableViewController for Subclass of. Click Next and then click Create to create the new class. In the Project Navigator, select RootViewController.swift and add the bold lines in the snippet that follows to add a few properties:

class RootViewController: UITableViewController {
private var familyNames: [String]!
private var cellPointSize: CGFloat!
private var favoritesList: FavoritesList!
private let familyCell = "FamilyName"
private let favoritesCell = "Favorites"

We’ll assign values to the first three of those properties from the outset, and then use them at various times while this class is in use. The familyNames array will contain a list of all the font families we’re going to display; the cellPointSize property will contain the font size that we want to use in all of our table view cells; and favoritesList will contain a pointer to the FavoritesList singleton. The last two are constants that represent the cell identifiers that we will use for the table view cells in this controller.

Set up all of this class’s properties by adding the bold code shown here to the viewDidLoad() method:

override func viewDidLoad() {
super.viewDidLoad()

familyNames = sorted(UIFont.familyNames() as [String])
let preferredTableViewFont =
UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
favoritesList = FavoritesList.sharedFavoriteList
}

In the preceding snippet, we populated familyNames by asking the UIFont class for all known family names, and then sorting the resulting array. We then used UIFont once again to ask for the preferred font for use in a headline. We did this using a piece of functionality added in iOS 7, which builds on the font size setting that can be specified in the Settings app. This dynamic font sizing lets the user set an overall font scaling for system-wide use. Here, we used that font’s pointSize property to establish a baseline font size that we’ll use elsewhere in this view controller. Finally, we grabbed the singleton favorites list object.

Before we go on, let’s delete the didReceiveMemoryWarning() method, as well as any commented-out table view delegate or data source methods—we’re not going to use any of them in this class.

The idea behind this view controller is to show two sections. The first section is a list of all available font families, each of which leads to a list of all the fonts in the family. The second selection is for favorites, and it contains just a single entry that will lead the user to a list of their favorite fonts. However, if the user has no favorites (for example, when the app is launched for the first time), we’d rather not show that second section at all, since it would just lead the user to an empty list. So, we’ll have to do a few things throughout the rest of this class to compensate for this eventuality. The first of these is to implement this method, which is called just before the root view controller’s view appears on the screen:

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}

The reason for this is that there may be times when the set of things we’re going to display might change from one viewing to the next. For example, the user may start with no favorites, but then drill down, view a font, set it as a favorite, and then come back out to the root view. At that time, we need to reload the table view, so that the second section will appear.

Next, we’re going to implement a sort of utility method for use within this class. At a couple of points, while configuring the table view via its data source methods, we’ll need to be able to figure out which font we want to display in a cell. We put that functionality into a method of its own:

func fontForDisplay(atIndexPath indexPath: NSIndexPath) -> UIFont? {
if indexPath.section == 0 {
let familyName = familyNames[indexPath.row]
let fontName = UIFont.fontNamesForFamilyName(familyName).first as String
return UIFont(name: fontName, size: cellPointSize)
} else {
return nil
}
}

This method uses the UIFont class, first to find all the font names for the given family name, and then later to grab the first font name within that family. We don’t necessarily know that the first named font in a family is the best one to represent the whole family, but it’s as good a guess as any.

Now, let’s move on to the meat of this view controller: the table view data source methods. First up, let’s look at the number of sections:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return favoritesList.favorites.isEmpty ? 1 : 2
return 0
}

We use the favorites list to determine whether we want to show the second section. Next, we tackle the number of sections in each row:

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return section == 0 ? familyNames.count : 1
return 0
}

That one’s also pretty simple. We just use the section number to determine whether the section is showing all family names, or a single cell linking to the list of favorites. Now let’s define one other method, an optional method in the UITableViewDataSource protocol that lets us specify the title for each of our sections:

override func tableView(tableView: UITableView,
titleForHeaderInSection section: Int) -> String? {
return section == 0 ? "All Font Families" : "My Favorite Fonts"
}

This is another straightforward method. It uses the section number to determine which header title to use. The final core method that every table view data source must implement is the one for configuring each cell, and ours looks like this:

override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
// The font names list
let cell = tableView.dequeueReusableCellWithIdentifier(familyCell,
forIndexPath: indexPath) as UITableViewCell
cell.textLabel.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel.text = familyNames[indexPath.row]
cell.detailTextLabel?.text = familyNames[indexPath.row]
return cell
} else {
// The favorites list
return tableView.dequeueReusableCellWithIdentifier(favoritesCell,
forIndexPath: indexPath) as UITableViewCell
}
}

When we created this class, we defined two different cell identifiers that we use to load two different cell prototypes from the storyboard (much like we loaded a table cell from a nib file in Chapter 8). We haven’t configured those cell prototypes yet, but we will soon! Next, we use the section number to determine which of those cells we want to show for the current indexPath. If the cell is meant to contain a font family name, then we put the family name into both its label and its detailLabel. We also use a font from the family (the one we get from thefontForDisplayAtIndexPath() method) within the text label, so that we’ll see the font family name shown in the font itself, as well as a smaller version in the standard system font.

Initial Storyboard Setup

Now that we have a view controller that we think should show something, let’s configure the storyboard to make things happen. Select Main.storyboard in the Project Navigator. You’ll see the navigation controller and the table view controller that we added earlier. The first thing we need to configure is the table view controller. By default, the controller’s class is set to UITableViewController. We need to change that to our root view controller class. In the Document Outline, select the yellow icon labeled Root View Controller, and then use the Identity Inspector to change the view controller’s Class to RootViewController.

The other configuration we’ll need to do right now is to set up a pair of prototype cells to match the cell identifiers we used in our code. From the start, the table view has a single prototype cell. Select it and press imageD to duplicate it, and you’ll see that you now have two cells. Select the first one, and then use the Attributes Inspector to set its Style to Subtitle, its Identifier to FamilyName, and its Accessory to Disclosure Indicator. Next, select the second prototype cell, and then set its Style to Basic, its Identifier to Favorites, and its Accessory to Disclosure Indicator, Also, double-click the title shown in the cell itself and change the text from Title to Favorites.

Tip The prototype cells that we are using in this example both have standard table view cell styles. If you set the Style to Custom, you can design the layout of the cell right in the cell prototype, just as you created a cell in a nib file in Chapter 8.

Now build and run this app on your device or the simulator, and you should see a nice list of fonts. Scroll around a bit and you’ll see that not all of the fonts produce text of the same height. Scroll right to the end, for example, and you’ll see that the sample text for the Zapfino font is much larger than all the others, as shown in Figure 9-7. Despite this, all of the cells are tall enough to contain their content, even though we didn’t do anything special to make this happen.

image

Figure 9-7. The root view controller displays the installed font families

As you saw in Chapter 8, this is because of a new feature in iOS 8 that calculates the correct cell height for cells that obey certain rules. Here, we are using standard table view cell styles, which follow the rules out of the box. In earlier versions of iOS, you would have had to implement theUITableViewDelegate protocol method tableView(_, heightForRowAtIndexPath:) to achieve the same result.

First Subcontroller: The Font List View

Our app currently just shows a list of font families, and nothing more. We want to add the ability for a user to touch a font family and see all the fonts it contains, so let’s make a new view controller that can manage a list of fonts. Use Xcode’s new file assistant to create a new Cocoa Touch class called FontListViewController as a subclass of UITableViewController. In the Project Navigator, select FontListViewController.swift and add the following properties:

class FontListViewController: UITableViewController {
var fontNames: [String] = []
var showsFavorites:Bool = false
private var cellPointSize: CGFloat!
private let cellIdentifier = "FontName"

The fontNames property is what we’ll use to tell this view controller what to display. We also created a showsFavorites property that we’ll use to let this view controller know if it’s showing the list of favorites instead of just a list of fonts in a family, since this will be useful later on. We’ll use the cellPointSize property to hold the preferred display size for displaying each font, once again using UIFont to find the preferred size. Finally, cellIdentifier is the identifier used for the table view cells in this controller.

To initialize the cellPointSize property, add the following code in bold to the viewDidLoad() method:

override func viewDidLoad() {
super.viewDidLoad()

// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false

// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()

let preferredTableViewFont =
UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
}

The next thing we want to do is create a little utility method for choosing the font to be shown in each row, similar to what we have in RootViewController. Here it’s a bit different, though. Instead of holding onto a list of font families, in this view controller we’re holding onto a list of font names, and we’ll use the UIFont class to get each named font, like this:

func fontForDisplay(atIndexPath indexPath: NSIndexPath) -> UIFont {
let fontName = fontNames[indexPath.row]
return UIFont(name: fontName, size: cellPointSize)!
}

Now it’s time for a small addition in the form of a viewWillAppear() implementation. Remember how, in RootViewController, we implemented this method in case the list of favorites might change, requiring a refresh? Well, the same applies here. This view controller might be showing the list of favorites, and the user might switch to another view controller, change a favorite (we’ll get there later), and then come back here. We need to reload the table view then, and this method takes care of that:

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if showsFavorites {
fontNames = FavoritesList.sharedFavoriteList.favorites
tableView.reloadData()
}
}

The basic idea is that this view controller, in normal operation, is passed a list of font names before it displays, and that the list stays the same the whole time this view controller is around. In one particular case (which you’ll see later), this view controller needs to reload its font list.

Moving on, we delete the numberOfSectionsInTableView() method entirely. We’ll only have one section here, and just skipping that method is the equivalent of implementing it and returning 1. Next, we implement the two other main data source methods, like this:

override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return fontNames.count
return 0
}

override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier,
forIndexPath: indexPath) as UITableViewCell

cell.textLabel.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel.text = fontNames[indexPath.row]
cell.detailTextLabel?.text = fontNames[indexPath.row]

return cell
}

Neither of these methods really needs any explanation, because they are similar to what we used in RootViewController, but even simpler.

We’ll add some more to this class later, but first we want to see it in action. To make this happen, we’ll need to configure the storyboard some more, and then make some modifications to RootViewController. Switch over to Main.storyboard to get started.

Storyboarding the Font List

The storyboard currently contains a table view controller that displays the list of font families, embedded inside a navigation controller. We need to add one new layer of depth to incorporate the view controller that will display the fonts for a given family. Find a Table View Controller in the Object Library and drag one out into the editing area, to the right of the existing table view controller. Select the new table view controller and use the Identity Inspector to set its class to FontListViewController. Select the prototype cell in the table view and open the Attributes Inspector to make some adjustments. Change its Style to Subtitle, its Identifier to FontName, and its Accessory to Detail Disclosure. Using the detail disclosure accessory will let rows of this type respond to two kinds of taps so that users can trigger two different actions, depending on whether they tap the accessory or any other part of the row.

One way to make a user action in one view controller cause the instantiation and display of another view controller is to create a segue connecting the two of them. This is probably an unfamiliar word for many people, so let’s get this out of the way: segue essentially means “transition,” and it is sometimes used by writers and filmmakers to describe making a smooth movement from one paragraph or scene to the next. Apple could have been a little straightforward and just called it a transition; but since that word appears elsewhere in the UIKit APIs, maybe Apple decided to use a distinct term to avoid confusion. We should also mention here that the word “segue” is pronounced exactly the same as the name of the Segway personal transportation product (and now you know why the Segway is called that).

Often, segues are created entirely within Interface Builder. The idea is that an action in one scene can trigger a segue to load and display another scene. If you’re using a navigation controller, the segue can push the next controller onto the navigation stack automatically. We’ll be using this functionality in our app, starting right now!

In order for the cells in the root view controller to make the Font List View Controller appear, you need to create a couple of segues connecting the two scenes. This is done simply by Control-dragging from the first of the two prototype cells in the Fonts scene over to the new scene; you’ll see the entire scene highlight when you drag over it, indicating it’s ready to connect, as shown in Figure 9-8.

image

Figure 9-8. Creating a show segue from the font list controller to the font names controller

Release the mouse button and select show from the Selection Segue section of the pop-up menu that appears. Now do the same for the other prototype cell. Creating these segues means that as soon as the user taps any of these cells, the view controller at the other end of the connection will be allocated and made ready.

Making the Root View Controller Prepare for Segues

Save your changes and switch back to RootViewController.swift. Note that we’re not talking about our latest class, FontListViewController, but instead its “parent” controller. This is the place where you’ll need to respond to the user’s touches in the root table view by preparing the new FontListViewController (specified by one of the segues you just created) for display and by passing it the values it needs to display.

The actual preparation of the new view controller is done using the prepareForSegue(_, sender:) method. Add an implementation of this method as shown here:

// MARK: Navigation

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
let indexPath = tableView.indexPathForCell(sender as UITableViewCell)!
let listVC = segue.destinationViewController as FontListViewController

if indexPath.section == 0 {
// Font names list
let familyName = familyNames[indexPath.row]
listVC.fontNames = sorted(
UIFont.fontNamesForFamilyName(familyName) as [String])
listVC.navigationItem.title = familyName
listVC.showsFavorites = false
} else {
// Favorites list
listVC.fontNames = favoritesList.favorites
listVC.navigationItem.title = "Favorites"
listVC.showsFavorites = true
}
}

This method uses the sender (the UITableViewCell that was tapped) to determine which row was tapped and asks the segue for its destinationViewController, which is the FontListViewController instance that is about to be displayed. We then pass some values along to the new view controller, depending on whether the user tapped a font family (section 0) or the favorites cell (section 1). As well as setting the custom properties for the target view controller, we also access the controller’s navigationItem property in order to set a title. ThenavigationItem property is an instance of UINavigationItem, which is a UIKit class that contains information about what should be displayed in the navigation bar for any given view controller.

Now run the app. You’ll see that touching the name of any font family shows you a list of all the individual fonts it contains, as seen in Figure 9-3. Furthermore, you can tap the Fonts label in the header of the fonts list navigation controller to go back to its parent controller to select another font.

Creating the Font Sizes View Controller

What you’ll notice, however, is that the app currently doesn’t let you go any further. Figures 9-4 and 9-5 show additional screens that let you view a chosen font in various ways, and we’re not there yet. But soon, we will be! Let’s create the view shown in Figure 9-4, which shows multiple font sizes at once. Using the same steps as you used to create FontListViewController, add a new view controller that subclasses UITableViewController, and name it FontSizesViewController. The only parameter this class will need from its parent controller is a font. We’ll also need a couple of private properties.

For starters, switch over to FontSizesViewController.swift and go ahead and delete the viewDidLoad, didReceiveMemoryWarning, and numberOfSectionsInTableView: methods, along with all of the commented-out methods at the bottom. Again, you’re not going to need any of those. Now add the following properties at the top of the class definition:

import UIKit

class FontSizesViewController: UITableViewController {
var font: UIFont!
private var pointSizes: [CGFloat] {
struct Constants {
static let pointSizes: [CGFloat] = [
9, 10, 11, 12, 13, 14, 18, 24, 36, 48, 64, 72, 96, 144
]
}
return Constants.pointSizes
}
private let cellIdentifier = "FontNameAndSize"

The font property will be set by FontListViewController before it pushes this view controller onto the navigation controller’s stack. The pointSizes property is an array of point sizes in which the font will be displayed. This really should be a class property with a fixed initial value, but Swift does not (yet) support that, so we used the same technique that you saw earlier to simulate that by using a static property of a structure. We also need the following utility method, which gets a version of a font with a given size, based on a table row index:

func fontForDisplay(atIndexPath indexPath: NSIndexPath) -> UIFont {
let pointSize = pointSizes[indexPath.row]
return font.fontWithSize(pointSize)
}

For this view controller, we’re going to skip the method that lets us specify the number of sections to display, since we’re going to just use the default number (1). However, we must implement the methods for specifying the number of rows and the content of each cell. Here are those two methods:

override func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return pointSizes.count
return 0
}

override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier,
forIndexPath: indexPath) as UITableViewCell

cell.textLabel.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel.text = font.fontName
cell.detailTextLabel?.text = "\(pointSizes[indexPath.row]) point"

return cell
}

There’s really nothing in any of these methods we haven’t seen before, so let’s move on to setting up the GUI for this.

Storyboarding the Font Sizes View Controller

Go back to Main.storyboard and drag another Table View Controller into the editing area. Use the Identity Inspector to set its class to FontSizesViewController. You’ll need to make a segue connection from its parent, the FontListViewController. So find that controller and Control-drag from its prototype cell to the newest view controller, and then select show from the Selection Segue section of the pop-up menu that appears. Next, select the prototype cell in the new scene you just added, and then use the Attributes Inspector to set its Style to Subtitle and its Identifierto FontNameAndSize.

Making the Font List View Controller Prepare for Segues

Now, just like the last time we extended our storyboard’s navigation hierarchy, we need to jump up to the parent controller so that it can configure its child. That means we need to go to FontListViewController.swift and implement the prepareForSegue(_, sender:) method like this:

// MARK: Navigation

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
let tableViewCell = sender as UITableViewCell
let indexPath = tableView.indexPathForCell(tableViewCell)!
let font = fontForDisplay(atIndexPath: indexPath)

let sizesVC = segue.destinationViewController as FontSizesViewController
sizesVC.title = font.fontName
sizesVC.font = font
}

That probably all looks pretty familiar by now, so we won’t dwell on it further.

Run the app, select a font family, select a font (by tapping a row anywhere except the accessory on the right), and you’ll now see the multisize listing shown in Figure 9-4.

Creating the Font Info View Controller

The final view controller we’re going to create is the one shown in Figure 9-5. This one isn’t based on a table view. Instead, it features a large text label, a slider for setting text size, and a switch for toggling whether this font should be included in the list of favorites. Create a new Cocoa Touch class in your project using UIViewController as the superclass, and then name it FontInfoViewController. Like most of the other controllers in this app, this one needs to have a couple of parameters passed in by its parent controller. Enable this by defining these properties and four outlets that we’ll use when we construct the user interface in FontInfoViewController.swift:

class FontInfoViewController: UIViewController {
var font: UIFont!
var favorite: Bool = false
@IBOutlet weak var fontSampleLabel: UILabel!
@IBOutlet weak var fontSizeSlider: UISlider!
@IBOutlet weak var fontSizeLabel: UILabel!
@IBOutlet weak var favoriteSwitch: UISwitch!

Next, implement viewDidLoad() and a pair of action methods that will be triggered by the slider and switch, respectively:

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view.

fontSampleLabel.font = font
fontSampleLabel.text =
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVv"
+ "WwXxYyZz 0123456789"
fontSizeSlider.value = Float(font.pointSize)
fontSizeLabel.text = "\(Int(font.pointSize))"
favoriteSwitch.on = favorite
}

@IBAction func slideFontSize(slider: UISlider) {
let newSize = roundf(slider.value)
fontSampleLabel.font = font.fontWithSize(CGFloat(newSize))
fontSizeLabel.text = "\(Int(newSize))"
}

@IBAction func toggleFavorite(sender: UISwitch) {
let favoritesList = FavoritesList.sharedFavoriteList
if sender.on {
favoritesList.addFavorite(font.fontName)
} else {
favoritesList.removeFavorite(font.fontName)
}
}

These methods are all pretty straightforward. The viewDidLoad() method sets up the display based on the chosen font; slideFontSize() changes the size of the font in the fontSampleLabel label based on the value of the slider; and toggleFavorite() either adds the current font to the favorites list or removes it from the favorites list, depending on the value of the switch.

Storyboarding the Font Info View Controller

Now head back over to Main.storyboard to build the GUI for this app’s final view controller. Use the Object Library to find a plain View Controller. Drag it into the editing area and use the Identity Inspector to set its class to FontInfoViewController. Next, use the Object Library to find some more objects and drag them into your new scene. You need three labels, a switch, and a slider. Lay them out roughly, as shown in Figure 9-9.

image

Figure 9-9. Each of the labels here has been given a light-gray background color, just for purposes of this illustration. Yours should have white backgrounds

Notice that we left some space above the upper label, since we’re going to end up having a navigation bar up there. Also, we want the upper label to be able to display long pieces of text across multiple lines, but by default the label is set to show only one line. To change that, select the label, open the Attributes Inspector, and set the number in the Lines field to 0.

Figure 9-8 also shows changed text in the lower two labels. Go ahead and make the same changes yourself. What you can’t see here is that the Attributes Inspector was used to right-align both of them. You should do the same, since they both have layouts that essentially tie them to their right edges. Also, select the slider at the bottom, and then use the Attributes Inspector to set its Minimum to 1 and its Maximum to 200.

Now it’s time to wire up all the connections for this GUI. Start by selecting the view controller and opening the Connections Inspector. When we have so many connections to make, the overview shown by that inspector is pretty nice. Make connections for each of the outlets by dragging from the small circles next to favoriteSwitch, fontSampleLabel, fontSizeLabel, and fontSizeSlider to the appropriate objects in the scene. In case it’s not obvious, fontSampleLabel should be connected to the label at the top, fontSizeLabel to the label at the bottom right, and the favoriteSwitchand fontSizeSlider outlets to the only places they can go. To connect the actions to the controls, you can continue to use the Connections Inspector. In the Received Actions section of the Connections Inspector for the view controller, drag from the little circle next to slideFontSize: over to the slider, release the mouse button, and select Value Changed from the context menu that appears. Next, drag from the little circle next to toggleFavorite: over to the switch and again select Value Changed.

One more thing we need to do here is create a segue so that this view can be shown. Remember that this view is going to be displayed whenever a user taps the detail accessory (the little blue “i” in a circle) when the Font List View Controller is displayed. So, find that controller, Control-drag from its prototype cell to the new font info view controller you’ve been working on, and select show from the Accessory Action section of the context menu that appears. Note that we just said Accessory Action, not Selection Segue. The accessory action is the segue that is triggered when the user taps the detail accessory, whereas the selection segue is the segue that is triggered by a tap anywhere else in the row. We already set this cell’s selection segue to open a FontSizesViewController.

Now we have two different segues that can be triggered by touches in different parts of a row. Since these will present different view controllers, with different properties, we need to have a way to differentiate them. Fortunately, the UIStoryboardSegue class, which represents a segue, has a way to accomplish this: we can use an identifier, just as we do with table view cells!

All you have to do is select a segue in the editing area and use the Attributes Inspector to set its Identifier. You may need to shift your scenes around a bit, so that you can see both of the segues that are snaking their way out of the right-hand side of the Font List View Controller. Select the one that’s pointing at the Font Sizes View Controller and set its Identifier to ShowFontSizes, as shown in Figure 9-10. Next, select the one that’s pointing at the Font Info View Controller and set its Identifier to ShowFontInfo.

image

Figure 9-10. Configuring the segues from the Font List View Controller

Setting Up Constraints

Setting up that segue lets Interface Builder know that our new scene will be used within the context of the navigation controller like everything else, so that scene automatically receives a blank navigation bar at the top. Now that the real confines of our view are in place, it’s a good time to set up the constraints. This is a fairly complex view with several subviews, especially near the bottom, so we can’t quite rely on the system’s automatic constraints to do the right thing for us. We’ll use the Pin button at the bottom of the editing area and the pop-up window it triggers to build most of the constraints we’ll need.

Start with the uppermost label. Click Pin, and then, in the pop-up window, select the little red bars above, to the left, and to the right of the little square—but not the one below it. Now click the Add 3 Constraints button at the bottom.

Next, select the slider at the bottom and click the Pin button. This time, select the red bars below, to the left, and to the right of the little square—but not the one above it. Again, click Add 3 Constraints to put them in place.

For each of the two remaining labels and for the switch, follow this procedure: select the object, click Pin, select the red bars below and to the right of the little square, turn on the check boxes for Width and Height, and finally, click Add 4 Constraints. Setting those constraints for all three of those objects will bind them to the lower-right corner.

There’s just one more constraint to make. We want the top label to grow to contain its text, but to never grow so large that it overlaps the views at the bottom. We can accomplish this with a single constraint! Control-drag from the upper label to the Include in favorites label, release the mouse button, and select Vertical Spacing from the context menu that appears. Next, click the new constraint to select it (it’s a blue vertical bar connecting the two labels) and open the Attributes Inspector, where you’ll see some configurable attributes for the constraint. Change the Relationpop-up to Greater Than or Equal, and then set the Constant value to 10. That ensures that the expanding upper label won’t push past the other views at the bottom.

Adapting the Font List View Controller for Multiple Segues

Now head back over to good old FontListViewController.swift. Since this class will now be able to trigger segues to two different child view controllers, you need to adapt the prepareForSegue(_, sender:) method, as shown here:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
let tableViewCell = sender as UITableViewCell
let indexPath = tableView.indexPathForCell(tableViewCell)!
let font = fontForDisplay(atIndexPath: indexPath)

if segue.identifier == "ShowFontSizes" {
let sizesVC = segue.destinationViewController as FontSizesViewController
sizesVC.title = font.fontName
sizesVC.font = font
} else {
let infoVC = segue.destinationViewController as FontInfoViewController
infoVC.font = font
infoVC.favorite = contains(FavoritesList.sharedFavoriteList.favorites,
font.fontName)
}
}

Now run the app and let’s see where we are! Select a font family that contains many fonts (for example, Gill Sans), and then tap the middle of the row for any font. You’ll be taken to the same list you saw earlier, which shows the font in multiple sizes. Press the navigation button at the upper left (It’s labeled Gill Sans) to go back, and then tap another row; however, this time tap on the right-hand side where the detail accessory is shown. This should bring up the final view controller, which shows a sample of the font with a slider at the bottom that lets you pick whatever size you want.

Also, you can now use the Include in favorites switch to mark this font as a favorite. Do that, and then hit the navigation button at the top-left corner a couple of times to get back to the root controller view.

My Favorite Fonts

Scroll down to the bottom of the root view controller, and you’ll see something new: the second section is now there, as you can see in Figure 9-11.

image

Figure 9-11. Now that we’ve picked at least one favorite font, we can see a list of them by tapping the new row that appears at the bottom of the root view controller

Tap the Favorites row, and you’ll see a listing of any fonts you’ve chosen as favorites. From there, you can do the same things you could do with the other font listing: you can tap a row to see a list of multiple font sizes, or you can tap a detail accessory to see the slider-adjustable font view and the favorites switch. You can even try turning off that switch and hitting the back button, and you’ll see that the font you were just looking at is no longer listed.

Table View Niceties

Now the basic functionality of our app is complete. But before we can really call it a day, there are a couple more features we should implement. If you’ve been using iOS for a while, you’re probably aware that you can often delete a row from a table view by swiping from right to left. For example, in Mail you can use this technique to delete a message in a list of messages. Performing this gesture brings up a small GUI, right inside the table view row. This GUI asks you to confirm the deletion, and then the row disappears and the remaining rows slide up to fill the gap. That whole interaction—including handling the swipe, showing the confirmation GUI, and animating any affected rows—is taken care of by the table view itself. All you need to do is implement two methods in your controller to make it happen.

Also, the table view provides easy functionality for letting the user reorder rows within a table view by dragging them up and down. As with swipe-to-delete, the table view takes care of the entire user interaction for us. All we have to do is one line of setup (to create a button that activates the reordering GUI), and then implement a single method that is called when the user has finished dragging. The table view gives us so much for free, it would be criminal not to use it!

Implementing Swipe-to-Delete

In this app, the FontListViewController class is a typical example of where this feature should be used. Whenever the app is showing the list of favorites, we should let the user delete a favorite with a swipe, saving them the step of tapping the detail accessory and then turning off the switch. Select FontListViewController.swift in Xcode to get started. Both of the methods we need to implement are already included in each view controller source file by default, but they are commented out. We’re going to uncomment each of them and provide them with real implementations.

Start by adding an implementation of the tableView(_, canEditRowAtIndexPath:) method:

override func tableView(tableView: UITableView,
canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return showsFavorites
}

That method will return true if it’s showing the list of favorites, and false otherwise. This means that the editing functionality that lets you delete rows is only enabled while displaying favorites. If you were to try to run the app and delete rows with just this change, you wouldn’t see any difference. The table view won’t bother to deal with the swipe gesture because it sees that we haven’t implemented the other method that is required to complete a deletion. So, let’s put that in place, too. Add an implementation for the tableView(_, commitEditingStyle:, forRowAtIndexPath:) method as follows:

override func tableView(tableView: UITableView,
commitEditingStyle editingStyle: UITableViewCellEditingStyle,
forRowAtIndexPath indexPath: NSIndexPath) {
if !showsFavorites {
return
}

if editingStyle == UITableViewCellEditingStyle.Delete {
// Delete the row from the data source
let favorite = fontNames[indexPath.row]
FavoritesList.sharedFavoriteList.removeFavorite(favorite)
fontNames = FavoritesList.sharedFavoriteList.favorites

tableView.deleteRowsAtIndexPaths([indexPath],
withRowAnimation: UITableViewRowAnimation.Fade)
}
}

This method is pretty straightforward, but there are some subtle things going on. The first thing we do is check to make sure we’re showing the favorites list; and if not, we just bail. Normally, this should never happen, since we specified with the previous method that only the favorites list should be editable. Nevertheless, we’re doing a bit of defensive programming here. After that, we check the editing style to make sure that the particular edit operation we’re going to conclude really was a deletion. It’s possible to do insertion edits in a table view, but not without additional setup that we’re not doing here, so we don’t need to worry about other cases. Next, we determine which font should be deleted, remove it from the FavoritesList singleton, and update our local copy of the favorites list.

Finally, we tell the table view to delete the row and make it disappear with a visual fade animation. It’s important to understand what happens when you tell the table view to delete a row. Intuitively, you might think that calling that method would delete some data, but that’s not what happened. In fact, we’ve already deleted the data! That final method call is really our way of telling the table view, “Hey, I’ve made a change, and I want you to animate away this row. Ask me if you need anything more.” When that happens, the table view will start animating any rows that are below the deleted row by moving them up, which means that it’s possible that one or more rows that were previously off-screen will now come on-screen, at which time it will indeed ask the controller for cell data via the usual methods. For that reason, it’s important that our implementation of the tableView(_, commitEditingStyle:, forRowAtIndexPath:) method makes necessary changes to the data model (in this case, the FavoritesList singleton) before telling the table view to delete a row.

Now run the app again, make sure you have some favorite fonts set up, and then go into the Favorites list and delete a row by swiping from right to left. The row slides partly off-screen, and a Delete button appears on the right (see Figure 9-12). Tap the Delete button, and the row goes away.

image

Figure 9-12. A favorite font row with the Delete button showing

Implementing Drag-to-Reorder

The final feature we’re going to add to the font list will let users rearrange their favorites just by dragging them up and down. In order to accomplish this, we’re going to add one method to the FavoritesList class, which will let us reorder its items however we want. OpenFavoritesList.swift and add the following method:

func moveItem(fromIndex from: Int, toIndex to: Int) {
let item = favorites[from]
favorites.removeAtIndex(from)
favorites.insert(item, atIndex: to)
saveFavorites()
}

This new method provides the underpinnings for what we’re going to do. Now select FontListViewController.swift and add the following lines at the end of the viewDidLoad method:

if showsFavorites {
navigationItem.rightBarButtonItem = editButtonItem()
}

We’ve mentioned the navigation item previously. It’s an object that holds the information about what should appear in the navigation bar for a view controller. It has a property called rightBarButtonItem that can hold an instance of UIBarButtonItem, a special sort of button meant only for navigation bars and tool bars. Here, we’re pointing that at editButtonItem, a property of UIViewController that gives us a special button item that’s preconfigured to activate the table view’s editing/reordering GUI.

With that in place, try running the app again and go into the Favorites list. You’ll see that there’s now an Edit button in the upper-right corner. Pressing that button toggles the table view’s editing GUI, which right now means that each row acquires a delete button on the left, while its content slides a bit to the right to make room. This enables yet another way that users can delete rows, using the same methods we already implemented.

But our main interest here is in adding reordering functionality. For that, all we need to do is add the following method in FontListViewController.swift:

override func tableView(tableView: UITableView,
moveRowAtIndexPath sourceIndexPath: NSIndexPath,
toIndexPath destinationIndexPath: NSIndexPath) {
FavoritesList.sharedFavoriteList.moveItem(fromIndex: sourceIndexPath.row,
toIndex: destinationIndexPath.row)
fontNames = FavoritesList.sharedFavoriteList.favorites
}

This method is called as soon as the user finishes dragging a row. All we do here is tell the FavoritesList singleton to do the reordering, and then refresh our list of font names, just as we did after deleting an item. To see this in action, run the app, go into the Favorites list, and tap theEdit button. You’ll see that the edit mode now includes little “dragger” icons on the right side of each row (see Figure 9-13), and you can use the draggers to rearrange items.

image

Figure 9-13. The favorite font list with reordering controls enabled

With that, our app is complete! At least, it’s complete as far as this book is concerned. If you can think of more useful things to do with these fonts, have at it!

Breaking the Tape

This chapter was a marathon. And if you’re still standing, you should feel pretty darn good about yourself. Dwelling on these mystical table view and navigation controller objects is important because they are the backbone of a great many iOS applications, and their complexity can definitely get you into trouble if you don’t truly understand them.

As you start building your own tables, refer back to this chapter and the previous one, and don’t be afraid of Apple’s documentation, either. Table views are extraordinarily complex, and it would be impossible to cover every conceivable permutation; however, you should now have a very good set of table view building blocks that you can use as you design and build your own applications. As always, feel free to reuse this code in your own applications. It’s a gift from the authors to you. Enjoy!