Learning iOS Development: A Hands-on Guide to the Fundamentals of iOS Programming (2014)

Chapter 3. Introducing Storyboards

Unlike the simple HelloWorld example and CarValet app you started in Chapter 2, “Objective-C Boot Camp,” most apps include multiple screens. Each screen may include buttons, labels, images, and even custom views. It is possible to create each screen in code: allocating each visual element, setting appropriate bounds, and configuring any specific properties such as text, state, and color. For many, a visual editor is a much better fit for matching design concepts to actual interface layout, saving a lot of effort.

Storyboards enable you to lay out your screens and the major ways users move from one screen to another. This chapter introduces you to the process of designing and laying out a storyboard. You start by exploring the scene, a single interface element that presents a complete and coherent user view. From there, you add more scenes, learning different ways to transition from one scene to the next and building a complex and nuanced app along the way. Finally, you learn and practice some ways to pass data between different scenes, an important part of the development process.

By the time you finish this chapter, you’ll know what it takes to architect apps with multiple scenes and to design the ways a user moves about the different scenes.

Storyboard Basics

To a user, the interface is your app. It is the primary gateway between the user and the functionality—the logic and data—powering your app. You design and implement the experience by creating informative and utilitarian elements, both the screens and the content of those screens. Each screen you design can do a number of things:

Image Display information, such as the number of car objects in the CarValet app

Image Enable actions, such as creating new cars by touching a button

Image Request input, such as specifying the make of a car

Image Show the result of user activities by updating the displayed car or switching to a screen that enables the user to edit cars

Every time someone uses an app, that user experiences a story. The story might focus on organizing their lives (Calendar), interacting with friends (Mail, social apps, and so on), or making the world safe against bad pigs (you know which apps those are).

App design happens on multiple levels. The best apps include holistic goals—not just how users interact with the pieces of information or activities, but how they interact with the app as a whole. Like a story, an app can be divided into scenes of naturally grouped activities of information. In Contacts, for example, there are scenes for looking at everyone and finding people, seeing someone in detail, and for editing someone.

Xcode supports this method of design with storyboards. With them, you can create and see all the scenes in your app and the flow between them. You edit storyboard files using a visual Layout editor that can have multiple controllers and manage connections between them. With the editor, you set the following:

Image The container class (view controller) for each screen

Image How a screen looks (the view objects)

Image The transition from one screen to another

Image How all the screens flow together to make your application

Scenes

The combination of a view controller and the view objects it contains is called a scene. You already created and modified a scene in the HelloWorld example in Chapter 2. Because you specified a Universal app when you created the project, Xcode created iPhone and iPad storyboards, each with an initial scene. As part of the example, you added a static label to the iPhone scene (refer to Figure 2-6).

Designing and building storyboards is a central part of app development. Many apps can use just one or two storyboards, one for each major supported form factor: handheld and tablet. And for those that are more complex, it is possible to use multiple storyboards for a form factor, flowing from one to the other, though that is beyond the scope of this book.

Storyboards fit well into the design process. First, you identify app functionality, divide the app into scenes for related functionality, and establish the flows between scenes. Then you conceptualize, build, and debug scenes, turning the design into the graphical user interfaces (GUIs) and the code that powers them.

In this chapter, you update the CarValet app so that it enables the user to create, view, and modify cars. The number of ways to divide and implement these three simple tasks is immense, if not infinite. For example, you could have one scene for adding cars, another for viewing them, and a third for editing. Or you could have a slight variation with a read-only editing overview scene that opens to a different scene depending on the type of value being edited: one scene for the text-based model and make, another for the year, and one more for the fuel level; and as you will do here, combine adding and viewing into one scene, with another scene for editing.

iOS provides a wealth of predefined user interface (UI) elements, and you can create many more. During this chapter, you learn about a few more elements in addition to the label, so your design will be relatively simple. As you progress through this book and add more elements to your toolset, your storyboard will grow more complex. For now, your app provides two scenes: one for adding and viewing cars and the other for editing a specific car.


A Note on User Experience

At this stage of the book, the goal is learning the basic pieces you need to create fully featured apps. Because of that, the focus is on learning how to use important elements such as storyboards and basic view classes, and hooking them together.

The emphasis is on using the individual elements, not on creating an app with the iOS 7 look and feel. Some of that comes in later as you start to put together what you have learned, though some elements, such as translucency, scrolling under bars, dynamics, and Text Kit, are more advanced topics.


Scene 1: Creating the Add/View Scene

Your first scene enables the valet attendant to create cars and view individual cars. Open either the project you used for Chapter 2 or use the CH03 CarValet Starter project from the sample code for this chapter.

Creating a scene is done in roughly three phases: add the visual elements, connect those elements to view controllers, and then implement scene behaviors using those elements. In practice, this tends to be an iterative process.

Step one is adding views to create the add/view car scene.

Adding the Add/View Visual Elements

Select Main_iPhone.storyboard from the list of files in the Project Navigator in Xcode. Xcode opens a Storyboard editor similar to Figure 3-1. The storyboard files were added when Xcode created the project from the Single View project template. As of the release of Xcode used for this book, all project templates except Empty add storyboard files.

Image

Figure 3-1 The Storyboard editor

Looking at the left side of Figure 3-1 just next to the Project Navigator, you can see that the storyboard already contains one scene called View Controller Scene. Again, Xcode created this along with ViewController.m and ViewController.h, the class for the scene’s view controller. These classes and storyboards provide a jumping-off point for implementing your design. They also provide an example of how Xcode helps you by creating starting points for adding your new elements and classes.

The application already works: build and run the app in the iPhone Simulator. Make sure the simulator will use the larger phone form factor by checking the right-hand side of the scheme popup. It should say iPhone (Retina 4-inch). If the menu shows something else, change the selection, and run the app again if it is already running.

The simulator shows a blank screen with a status bar that looks like the screen in the Storyboard editor. But how did the system know which scene to show first, when most storyboards have multiple scenes? The answer is the gray arrow on the left side of the Interface Builder (IB) window inFigure 3-1. It points to the scene to show when the app has finished launching.

Adding the First Label: Xcode Guides

Informational labels provide important pieces of information to the user. If the app is about parking cars, “How many cars are parked?” and “How many cars have I ever parked?” are important questions to answer. For now, the label answers the second question. To change from a blank screen, add the first visual element for the interface: the label showing the total number of cars.

The goal is for the label to be at the top of, and stretch across, the width of the app screen—but not right at the top or the full width. The Apple User Interface Guidelines recommend white space around various elements, including labels. Xcode helps conform to these guidelines by showing dashed blue guidelines as you move elements around the current screen. This includes snapping elements to those guidelines.

To see this in action, lay out the Total Cars label following these steps:

1. From the palette of objects on the lower right, drag a UILabel into the scene.

2. Move the label until the top and left of the label are flush with the top and left Xcode guidelines. The left side of Figure 3-2 shows the label and the guidelines before the mouse is released.

Image

Figure 3-2 Using Xcode guides to align the Total Cars label

3. Now grow the right side of the label until it is flush with the right guideline. Make sure the top and left edges of the label are still flush with their respective guideline. The right side of Figure 3-2 shows how things look just before you release the mouse.

4. Change the text and set the label font to Headline, as shown in Figure 3-3. The text and numbers are placeholders to help you lay out the interface. Your app updates them as values change at runtime. The font shows the text is important information.

Image

Figure 3-3 Adding the first label

Run the project again, and you see the Total Cars: 999 label that you just created.

In the CarValet app, the parking valet needs to create and view cars. Your app must supply critical information to help the valet do the job. Table 3-1 lists the visual elements needed, along with each element’s purpose and class. This table provides an excellent example of the kinds of information to capture and produce as you design apps. Always take care to document the purpose of visual elements as well as their relationships and behaviors. Although things can and will change as you create your apps, you’ll find documented information like Table 3-1 to be invaluable in speeding up your implementation.

Image

Table 3-1 Visual Elements in the Add/View Scene

Use Table 3-1 to add the remaining elements to your scene. When you are finished, your scene should look like the interface shown in Figure 3-4.

Image

Figure 3-4 Scene 1 visual elements

You might wonder why you want a divider view. Without the divider, users find it difficult to differentiate between the two sets of displayed information. Labels help here, but they don’t help enough. Without dividers, users may interpret each big header label as a different part of the same kind of information. See the sidebar “Adding the Visual Divider” for help adding it to your view controller.


Adding the Visual Divider

Using dividers is a common visual separation technique for categories of information. A divider differentiates the material on one side from the material on the other. The following steps enable you to add a divider to your scene (see Figure 3-5):

1. Drag a UIView element onto the scene and leave it selected.

2. In the Attributes inspector on the right of the storyboard area, set the background color to Light Gray, as shown in Figure 3-5.

3. Set the left edge of the view to the default inset from the main view. Then drag the right side to the default inset from the main view’s right edge. You can use the left and right guidelines in Xcode shown previously in Figure 3-2.

4. Change to the Size inspector and set the height to 2 points. The Size inspector icon looks like a ruler. In Figure 3-1, it is the fifth icon at the top of the utilities area on the right side of the Xcode project window.

5. Move the view until it is about halfway between the bottom of the Add Car button and the top of the Car Number label. Again, use the guidelines to make sure it stays centered. Another trick is to get the view close to where you want it and then use the arrow keys for fine movement. The arrow keys work with the selected view.

6. After you let go of the divider view, check to make sure the left and right edges are the correct inset from the main view. Also check that the divider still has a height of 2 points. Keep adjusting things until everything looks right.

Image

Figure 3-5 Setting the divider view background to light gray


When you have completed the layout, run the app to see your changes. If the screen still does not look balanced, try adjusting the layout and re-running the app until things look right.

Adding the Initial Add/View Behaviors

Every UI element you add to your app must show or do something relevant. Yes, some elements—such as the divider view and decorative images—serve other purposes. These decorations are useful only when used with items that show information, allow action, or show results of an action.

The interface you are working on now displays the total number of cars, the index of the car shown, and its detailed information. It also provides actions: creating a car and going to the next or previous car. Before you can create the code to respond to buttons or update labels, you need a way to know when the button is touched. You also need references to any labels you will change.

IBAction and IBOutlet

You use IB to add behaviors and link to displayed items by using the IBAction and IBOutlet identifiers. Their names give you hints about what they are for. Actions do something, and outlets connect things together. And both start with IB; they are not Objective-C types, but instead enable Xcode to create stand-ins that IB uses for connecting visual elements to properties and user actions to methods.

IBAction identifies methods that a UI element can call, usually in response to a user doing something with the interface. Buttons are touched, switches are toggled, or sliders are changed; the user interacting with a control on the screen can call an action.

IBOutlet identifies properties for UI elements. When connected, you use the property for updating labels, setting a value for a slider, or even hiding and showing views. Those same properties can be used to read user input: text from fields, whether a switch is on or off, or where a dragged view is.

When you design your app, you set up how things are connected and what kinds of actions they may take. After you lay out your interface, you create the actions and outlets to connect the code you write with the views the code updates or reads. For CarValet, the valet creates new cars, sees the information for a car, or types in a new make or model.

You need to update the labels with numbers or information. You also need to enable the user to create cars or navigate through them.

Creating an Outlet

You create outlets by Ctrl-dragging from a UI element to a source file, usually the class header. To learn how this is done, create an outlet for the total number of cars label.

First, open the Main_iPhone.storyboard file and then show the Assistant editor, either by selecting the middle button from the three editor buttons in the Xcode toolbar, selecting View > Assistant Editor > Show Assistant Editor, or pressing Cmd-Option-Return.

By default, the Assistant editor shows the appropriate matching file for whatever is in the Main editor window. When the main window is IB, it is the header file for the active view controller class. Right now, there is only one scene with a view controller class of ViewController, soViewController.h is shown in the Assistant.

Creating the connection happens in two steps: first, dragging out the new connection and then naming the connection. Add an IBOutlet for the Total Cars label using these steps:

1. Make sure that ViewController.h is showing in the Assistant editor.

If not, you can use the file popup at the top of the Assistant editor to change files. Click in the area highlighted in green in the top screen shot of Figure 3-6 and choose Automatic > ViewController.h.

2. Right-click (or Ctrl-click) and drag from the Total Cars label in IB to the properties area in ViewController.h in the Assistant editor, as shown in the top of Figure 3-6.

3. In the popup that appears, make sure the Connection type is Outlet and then enter totalCarsLabel for the name. The filled-out popup is shown in the middle of Figure 3-6.

4. Press Connect in the popup, and the property shown in the bottom of Figure 3-6 is created.

Image

Figure 3-6 Creating and connecting an IBOutlet

You can now use the totalCarsLabel property to access the label object in your scene. Setting the text is as simple as the following:

self.totalCarsLabel.text = @"New text!";

Creating an Action

Creating actions is almost identical. The only change is how you fill out the popup that appears after you have dragged out the connection. Add an IBAction for the New Car button using these steps:

1. Make sure you are viewing the iPhone storyboard in the Main editor and ViewController.h in the Assistant. If not, look at step 1 in the earlier section, “Creating an Outlet.”

2. Right-click (or Ctrl-click) the New Car button and drag to the methods area in ViewController.h. The convention is to group properties in the upper part of the class interface definition and methods in the lower part, as shown in the top part of Figure 3-7.

Image

Figure 3-7 Creating and connecting an IBAction

3. In the popup that appears after you release the mouse, set the Name of the method to newCar and the Connection type to Action, as shown in the middle of Figure 3-7.

4. The bottom part of Figure 3-7 shows the resulting declaration of the action method. A corresponding action method shell is created in the .m file.

If you forget to choose Action, it takes two steps to fix the problem. See the sidebar, “Important: Fixing a Forgotten Action Choice,” for more information.


Important: Fixing a Forgotten Action Choice

It is inevitable: You are specifying your outlets and actions, and you forget to choose Action for a button. Now your newCar method is an outlet instead. Your first attempt at a fix is to delete the newCar property and hook things up again.

Everything looks fine, but then you try to run the app. The result is a crash with something like the following near the top of a long stack trace in the console:

2013-07-21 14:20:10.180 CarValet[9257:a0b] *** Terminating app due to uncaught
exception 'NSUnknownKeyException', reason: '[<ViewController 0x8aa22e0>
setValue:forUndefinedKey:]: this class is not key value coding-compliant for the
key newCar.'

The clue is in the last part of the message. Something about “not key value coding-compliant for the key newCar.” That says there is a class property called newCar that the view is trying to hook up at runtime. It happens because deleting the property from the .h file does not update the information in IB. It is still trying to hook up the newCar outlet to the New Car button.

To fix this, you have to remove the IB information. In this case, you connected the outlet to the ViewController class, so that is where the problem lies:

1. In the Storyboard view, find the ViewController class in the left-hand list of view items and view controllers and then right-click or Ctrl-click that item.

Note that you can also do this using the yellow view controller symbol below the scene on the main IB canvas.

2. A popover that looks like Figure 3-8 appears. Find the item with the yellow warning triangle and click the “x” next to that item.

Image

Figure 3-8 Fixing a forgotten Action choice

Now you can build and run, and the app works correctly.

This mistake is not limited to beginners. It is one that even experienced iOS programmers make on a somewhat regular basis. Remember how to fix this one; you will need it.


Scene 1: Finish Adding Outlets and Actions

Now you can practice creating outlets and actions by adding connections for the rest of the UI elements in scene 1.

Table 3-2 is another example of helpful information you can record as you design your screens and behaviors. It shows the UI elements, how they are connected to your code, and the name of each connection. Use this table to set up outlets and actions for your add/view scene. In addition, practice creating an Outlet instead of an Action so you remember how to fix it (see the sidebar, “Important: Fixing a Forgotten Action Choice”).

Image

Table 3-2 Actions and Outlets for the Add/View Scene

As you added outlets, you might have noticed a new qualifier in the created properties:

@property (weak, nonatomic) IBOutlet UILabel *totalCarsLabel;

weak indicates object ownership and helps the compiler add code for memory management. It is important to understand what these memory qualifiers mean.

A Brief Diversion on Memory Management

iOS devices have a set amount of physical RAM, and unlike desktop systems, there is no virtual memory system making storage space appear to be infinite. Instead, iOS gives you the tools you need to easily manage the memory used by your app.

Each app gets a fixed amount of memory while it is running. The important part is not the exact amount—there is usually enough for quite complex apps—the important part is how you manage the memory. The reason is the system watchdog, an aptly named part of iOS that keeps an eye on all running applications and terminates any memory hogs.

Every object you allocate, from view controllers to cars, requires memory. And every object has a lifetime: It is created, used, and then no longer needed. After that last stage happens, the system can safely free up, or reclaim, the memory used by that object. But how do you indicate you are done with an object? More importantly, what if your class is not the only one using the object? Safe removal of the object means that nothing needs it, that nothing has a reference; or put another way, that no other object owns or needs the one to be freed.

iOS keeps track of how many other objects need a particular instance, and when that number reaches zero, it frees up the memory. In most cases, you do not need to do anything for this to work.

One simple example is creating temporary variables. Look at the following method:

- (void)usesATempString:(NSString*)inString {
    NSString *tempString = [inString copy];
    // do things with tempString
}

The tempString object is created from a copy of the method argument inString. copy creates a whole new object including allocating any required memory. But how does the system know when to free up the memory used by tempString?

The answer lies in how many other objects are referencing tempString. In the previous code, the string is allocated and used inside the method. No other objects refer to tempString, so it can be safely freed up when the method is done. The usesATempString: method is the only owner of tempString.

However, as you create custom view controllers with outlets or create larger model objects, the relationships get more complicated. There can be multiple users of a car object or several places referencing a view controller. You need a way to tell the OS the relationship between an instance and the objects it uses.

Memory-related property qualifiers are used for this purpose. You specify the relationship between the instance and object it uses using one of two qualifiers: strong or weak. (There are a few other qualifiers, but they are rarely used and Xcode often adds them for you.)

strong is the default qualifier and means ownership. The system cannot free the object until the owner is done with it. Generally, it says either: “I created this object and when I am done with it (or I am freed up), the system can free it,” or “I did not create this object, but I need it and you have to at least wait until I am done with it.”

Owners are done with objects when one of three things happens: The owner sets the variable holding the object (the reference) to nil; the owner sets the reference to another object; or the owner itself is freed up.

weak means the object is not owned, so it can be freed up as needed. This can sound dangerous, especially if your reference had a valid object and then that object gets freed. To deal with this situation, when the system frees an object with a weak reference, it sets the reference to nil. That way, you do not accidentally try to access memory in an unknown state. This is handy, especially because sending a message to a nil object works. For methods with return values, it just results in nil.


Tip: To Type Defaults or Not to Type Defaults?

That is the question. Whether ‘tis nobler in the code to suffer the bugs and problems of forgotten defaults, or to write extra words and, by writing, stop bugs... Okay, enough Shakespeare parody. The question is whether to include defaults, such as strong for a property, in your code.

Along with almost all the professional coders I know, I find it better to include them for two important reasons. When they are not there, you inevitably have to refer to the docs for the defaults. Worse, treating a property as weak when it is really strong—effectively pretending your object is not the owner when it really is—can lead to memory leaks that are hard to find as well as other possible memory issues.


As an example, Car might include a reference to the valet that parked it. The car object does not own the valet object, and there may not even be an assigned valet yet. The property is weak both because of ownership and because it is optional. A method that returns the name of the valet can deal with this situation with code such as the following:

NSString *valetName;
if (self.valet != nil) {
    valetName = self.valet.name
} else {
    valetName = @"No valet.";
}

If there is a valid valet object, the name is set to the name attribute of that valet. If not, the string is set to "No valet."


Warning: Memory Leaks and Retain Cycles

A leak happens when the memory is no longer needed, but it is not possible for the system to free that memory. Even with the sophisticated memory management in iOS, leaks can occur and are usually due to retain cycles. This happens when two objects have strong references to each other. Because strong references are set to nil only when the owning object is freed up, the two-way reference can never be set to nil. Each object is waiting for the other to be deallocated.

For example, imagine a screen showing all cars that then shows a viewer for an individual car. The individual car viewer can also show an editor. The classes are set up so that the editor can inform the viewer when it is done:

Image

The viewer and editor both have strong references to each other. When the editor is done, it lets the viewer know; the viewer closes the editor but fails to set myCarEditor to nil.

The system closes the car editor but is unable to free up the memory because some other object, in this case the car viewer, still has a strong reference. If the viewer opens another editor, the old one can be deallocated. This happens because assigning a new editor tomyCarEditor tells the system that the viewer is no longer interested in the old one. However, it is now tied to the new one.

But that is not the full cause of the memory leak. That happens at the AllCarsViewer level, and here is how:

Image The all cars viewer opens an individual car viewer.

Image That car viewer opens an editor.

Image The editor closes, and, as you have seen, the editor cannot be deallocated.

Image The car viewer closes, and the all cars viewer correctly cleans up by setting myCarViewer to nil.

At this point, the system is unable to free up the memory for the car viewer because another object, the car editor, has a strong reference. And the car viewer has a strong reference to that same car editor. Worse, there are no other references to either the viewer or editor becausemyCarViewer is now nil.

The result is two objects in memory, the viewer and editor, that cannot be deallocated. This can get especially tricky when it is not a simple object-to-object cycle where object A retains object B that also retains A. The more typical situation is a chain of objects, where the retain cycle happens between two or more of those objects at different levels of the hierarchy.

For more information, see “Retain Cycles” in Learning Objective-C 2.0, Second Edition, by Robert Clair, page 337.


Adding New Cars

Apps often show counts of things. CarValet displays the total number of cars. To show a total car count, you need to keep track of all Car objects. To display information for a particular car, you need to track the current car. Counting objects and displaying information are common display tasks in apps. You could accomplish them by using a reference to an actual Car object, or you could simply use an index into the array of cars. Remember that simpler is generally better and less error-prone. Open ViewController.m and change it so it looks like Listing 3-1, where the changes are shown in bold.

Listing 3-1 Adding and Initializing State Instance Variables


//  ViewController.m
//  CarValetScenes

#import "ViewController.h"

#import "Car.h"

@implementation ViewController {
    NSMutableArray *arrayOfCars;                 // 1
    NSInteger displayedCarIndex;                 // 2
}

- (void)viewDidLoad {
    [super viewDidLoad];

    arrayOfCars = [[NSMutableArray alloc] init]; // 3

    displayedCarIndex = 0;                       // 4
}


Here’s what happens in the numbered lines in Listing 3-1:

1. Use a mutable array to keep track of all the Car objects.

2. Specify the array index for the car displayed in the lower area.

3. Initialize the array of cars to an empty array.

4. Display the first car created.

Now that you have a place to hold new cars, you need to create them. The shell of newCar: was created for you when you set up the IBAction. You can tell that a method is an IBAction by looking at the declaration in either in the .h or .m file. The first part always looks like this:

- (IBAction)

When you drag a connection from a button to a view controller, Xcode looks through the controller’s source for IBAction and presents a list of those methods (among other things).

Actions that create new objects are very common in apps. Replace newCar: with Listing 3-2. The method shows a simple way to both create an object—in this case, a car—and update status.

Listing 3-2 newCar:


- (IBAction)newCar:(id)sender {
    Car *newCar = [[Car alloc] init];                              // 1

    [arrayOfCars addObject:newCar];

    NSString *totalCarText;
    totalCarText = [NSString stringWithFormat:@"Total Cars: %d",   // 2
                            [arrayOfCars count]];

    self.totalCarsLabel.text = totalCarText;                       // 3
}


Here’s what happens in the numbered lines in Listing 3-2:

1. Create a new car with default values and add it to the array.

2. Create a new total car string based on the current number of cars.

3. Update the string displayed to the valet.

Run the app. Because there are initially no cars, the label still says Total Cars: 999. This is a common mistake for first-time app coders. You could initialize the number to 0. Another solution is to create one car when the view loads, a technique you might use in your own apps.

You just finished adding code to create a new car from a button tap. You can simulate a tap by calling newCar: and not specifying a sender. To do this, add the following line to viewDidLoad: just after initializing arrayOfCars:

[self newCar:nil];

Now build and run the app again. The label shows Total Cars: 1.

Adding Car Display Behaviors

Displaying object information is another common task. A UILabel displays the string from its text attribute in the interface, using whatever font, size, and color you specify. When the text attribute changes, the label updates.

You need four things to use UILabel for displaying changing information: a label, a connection to that label, a string to display, and code to set the label’s text.

You have already added the car information label to your scene. You have also created a connection, an IBOutlet, to that label. Adding a description for a car can be done in a few ways. One is by adding a public method. Another is using a read-only property and custom getter. Implement the property and getter by following these steps:

1. In Car.h, add the following property declaration below fuelAmount (see Challenge 5 later in the chapter):

@property (readonly) NSString *carInfo;

2. Open Car.m and add this code for the custom getter just below initWithMake:...

- (NSString *)carInfo {
    return [NSString stringWithFormat:
                  @"Car Info\n    Make: %@\n    Model: %@\n    Year: %d",
                  self.make ? self.make : @"Unknown Make",
                  self.model ? self.model : @"Unknown Model",
                  self.year];
}

carInfo constructs a string with newlines “\n” between each category of information. The string either includes the make and the model or indicates that they are unknown. (See the following sidebar, “The Ternary Operator,” for how this works.)


The Ternary Operator

The ?: ternary operator used in this section defines a simple if-then-else conditional expression:

NSString *what = self.which ? @"Something" : @"Nothing";

This line checks if self.which evaluates to YES or NO. Note that non-nil is the same as YES, and nil the same as NO. If it is NO, it sets what to the string "Something"; otherwise to "Nothing". It is a shorthand way of writing the following code:

NSString *what;
if (self.which) {
    what = @"Something";
} else {
    what = @"Nothing";
}

Now that you can get a string describing the car information, you need a way to update the text of the car number and info labels. You could just set the text of each label every place you need to update information. However, you know that updating the displayed car information can happen from multiple places. When that happens, it is good design practice to abstract out the relevant code.

Abstracting out, or centralizing, commonly called code is a good way to reduce the overall amount of code in an app, as well as making maintenance and updates easier. If you change the way car information is displayed at a later date, you need to change only one routine in addition to making any interface changes. Without abstraction, you could be making multiple changes—and probably missing some.

Abstract out the display of car information by adding the displayCurrentCarInfo method from Listing 3-3 just above newCar:.

Listing 3-3 displayCurrentCarInfo


- (void)displayCurrentCarInfo {
    Car *currentCar;
    currentCar = [arrayOfCars objectAtIndex:displayedCarIndex];    // 1

    self.carInfoLabel.text = currentCar.carInfo;                   // 2

    NSString *carIndexText;                                        // 3
    carIndexText = [NSString stringWithFormat:@"Car Number: %d",
                                        displayedCarIndex + 1];
    self.carNumberLabel.text = carIndexText;
}


Here’s what happens in the numbered lines in Listing 3-3:

1. Load the object for the currently displayed car. Note that production code should check that displayedCarIndex is a valid index.

2. Get the car description using the carInfo property.

3. Update the number in the car label. Note that 1 is added to the current index. See the “Index Versus Count” sidebar for an explanation.


Index Versus Count

Array indexes begin at 0, but people think of the first item as being, well, first. Displaying the index as car number would show a 0 for the first car. This is an error that happens frequently.

To add confusion, the count method returns the number of objects in the array. It is effectively 1-based. If there are two cars in the array, you use an index of 1 for the second element, but count returns 2 for the number of items.

Just remember this: “Index is 0-based; count is 1-based.”


When the view opens, you create a car. When data is created or changed, it is good UI practice to show that data, in this case the car, to the user. Call displayCarInfo in viewDidLoad, just after the line that sets displayedCarIndex to 0:

[self displayCurrentCarInfo];

When you run the app this time, it almost works, but the car display label shows only “Car Info,” the first line of the string returned by carInfo. By default, a label is set to display one line of text. For tall labels that you know can show multiple lines and that need to show as much information as possible, set the lines to 0. This tells UILabel to show as many lines as can fit:

1. Open the storyboard.

2. Select the car display label. If needed, change the height so it roughly matches the height of the label shown in Figure 3-9.

Image

Figure 3-9 Changing label line count

3. In the attributes panel on the right-hand side of the storyboard, change the number of lines from the default of 1 to 0, as shown in Figure 3-9.

Setting the number of lines to 0 for a UILabel tells iOS to use as many lines as needed to display the text. Since the string returned by carInfo includes newline characters, the information for the selected car needs multiple lines. Run the app again to see the change.

Adding Previous and Next Car Buttons

You have given the user a way to create cars, and the app shows users car detail. But at the moment, you are always showing the same car. Now it is time to let the user choose which car to view. iOS has lots of ways to do this, and in Chapter 8, “Table Views I: The Basics,” you learn to use one of the most common ones. For now, you use Previous and Next buttons to switch cars.

Previous and Next car buttons work by changing the index of the current car, displayedCarIndex, to a new element in the arrayOfCars. Then they update the displayed info with the routine you wrote earlier. The only real difference between the buttons is that Previous subtracts 1 from the current index, and Next adds 1. Instead of writing two sets of code that do almost the same thing, you can use the power of abstraction to write one method that takes a new desired index, makes sure the new index is valid, and then updates displayedCarIndex if the updated new index is different.

changeDisplayedCar: is called by previousCar: and nextCar:. Add the code from Listing 3-4 above newCar:.

Listing 3-4 changeDisplayedCar:


- (void)changeDisplayedCar:(NSInteger)newIndex {
    if (newIndex < 0) {                            // 1
        newIndex = 0;
    } else if (newIndex >= [arrayOfCars count]) {  // 2
        newIndex = [arrayOfCars count] - 1;
    }

    if (displayedCarIndex != newIndex) {           // 3
        displayedCarIndex = newIndex;
        [self displayCurrentCarInfo];
    }
}


Here’s what happens in the numbered lines in Listing 3-4:

1. Make sure the new index is a valid index. If the index is less than 0, make it 0.

2. If newIndex is beyond the end of arrayOfCars, set it to the last element. Remember that “Index is 0-based; count is 1-based.”

3. Update the display only if the index has changed. Although code execution savings are not really necessary in this simple case, reducing code execution is a good habit to form. Anything you can do to preserve battery power ultimately benefits the user.

Now use changeDisplayedCar: in the action methods (see Listing 3-5).

Listing 3-5 Next and Previous Action Methods


- (IBAction)previousCar:(id)sender {
    [self changeDisplayedCar:displayedCarIndex - 1];
}

- (IBAction)nextCar:(id)sender {
    [self changeDisplayedCar:displayedCarIndex + 1];
}


changeDisplayedCar: is an example of code consolidation. Another example is displayCurrentCarInfo. Using less code has some important advantages:

Image Easier to maintain and debug

Image Generally performs better

Image Results in smaller applications and faster development

Another simple consolidation you can implement is setting the two count-based labels. Each displays a base string with a variable number. Look for consolidation opportunities when you start designing app behaviors. You can also do it as you refine the design and include things like a data model and more specific scene behaviors. For now, you are going through a more iterative process. Some things are well specified and others are not. As you implement, you make changes to the design and vice versa...you iterate the design and implementation.

Listing 3-6 shows a method for updating both count labels. Add updateLabel:withBaseString:count: to your app above displayCurrentCarInfo, and make the changes to the other methods that are shown in bold.

Listing 3-6 Adding updateLabel:withBaseString:count:


- (void)updateLabel:(UILabel*)theLabel
     withBaseString:(NSString*)baseString
              count:(NSInteger)theCount {
    NSString *newText;
    newText = [NSString stringWithFormat:@"%@: %d", baseString, theCount]; // 1

    theLabel.text = newText;                                               // 2
}

- (void)displayCurrentCarInfo {
    Car *currentCar;
    currentCar = [arrayOfCars objectAtIndex:displayedCarIndex];

    self.carInfoLabel.text = [currentCar carInfo];

    [self updateLabel:self.carNumberLabel
       withBaseString:@"Car Number"
                count:displayedCarIndex + 1];
}

- (IBAction)newCar:(id)sender {
    Car *newCar = [[Car alloc] init];

    [arrayOfCars addObject:newCar];

    [self updateLabel:self.totalCarsLabel
       withBaseString:@"Total Cars"
                count:[arrayOfCars count]];
}


Here’s what happens in the numbered lines in Listing 3-6:

1. The format string @"%@: %d" creates a string with the contents of baseString, followed by a colon and a space, and then the integer value of theCount. See the “Formatted Strings” sidebar for more information.

2. Sets the label to the newly generated string.

Run the app and try all the features. You can create new cars and look at each car. Try looking at the first car and tapping Previous, and then try looking at the last car and tapping Next. The last two actions test behavior that is just out of bounds. In this case, trying to show an element just before the beginning of the array of cars and an element just past the end. These kinds of tests are great for catching code errors because they are the ones most likely to result in some sort of crash.


Formatted Strings

You use dynamically created strings for many purposes, such as displaying updates to data, generating URLs, and greeting a user by name. Using stringWithFormat:, a class method on NSString, is the most common way to create formatted strings.

A format string is like a normal string, except there are placeholders for values of objects and variables. A string can be simple, with just one variable, or very complex, including multiple placeholders, newlines, and other special characters.

This is the basic syntax of the message:

[NSString stringWithFormat:FormatString, arg1arg2, ...];

The number of arguments is dynamic and must equal the number of placeholders in the string.

FormatString is an Objective-C string that has special placeholder characters for different types of values. For example, @"An Integer: %d\nString: '%@'" replaces the %d with the value of an integer variable and the %@ with the contents of a string object or the description of any object. The \n sequence inserts a newline.

All placeholders start with a percent symbol, then optional formatting, then a character that specifies the type of value. For example, in %0.2f, f specifies a floating-point value, and 0.2 displays only two decimal places of accuracy.

You can read more about format strings in the Apple documentation. Search for “String Format Specifiers.”


The add/view scene contains most of the elements it needs. Xcode set the container class to ViewController, a custom subclass of UIViewController, when the project was created. You added the following visual elements:

Image UILabels such as the total car count

Image UIButtons to add cars and show details about different cars

Image One UIView to provide some visual separation

The outlets, methods, and instance variables you create give the interface life.

Scene 2: Adding an Editor

In this section, you practice another core task in app development: laying out and coding a scene, and then hooking the new scene into an existing interface. For the edit scene, you add a new scene to the storyboard, add the views for displaying and editing a car, and create a custom controller for that scene. Next, you implement the transition to and from the view/edit scene.

If the valets using your app parked only one type of car, what you have already might be enough. Instead, cars can vary in make, model, and year, and your app needs to help the valet identify cars by using these criteria. You need a scene for editing cars.

The process you use for adding a new scene goes like this:

1. Create an object for managing the new scene. This is a subclass of one of the built-in iOS view controllers.

2. Add an iOS view controller of the same built-in type to the storyboard.

3. Set the class of this new controller to the one you set up in step 1.

4. Add the view elements to the new scene on the storyboard.

5. Create any actions or outlets needed by the scene.

6. Write the code to make it work and make sure it works.

At some point in the process, you also need to add any required transitions. Sometimes that happens at the end, and sometimes it happens earlier. Also, sometimes the controller is dragged out and some or all of the visual elements are added before the Objective-C class is added.

Now you need to create a container class. Make a new Cocoa Touch Objective-C Class file called CarEditViewController, a subclass of UIViewController. Make sure it is added to the CarValet target.

Now add the new scene:

1. Select Main_iPhone.storyboard to open the storyboard.

2. Drag a view controller object from the object palette onto the canvas as shown in Figure 3-10.

Image

Figure 3-10 Dragging a UIViewController onto the storyboard canvas

3. In the Identity inspector, set Class to CarEditViewController (see Figure 3-11).

Image

Figure 3-11 Setting the Scene 2 custom class

Adding the Editor Visual Elements

Any app that shows values probably needs to edit at least some of them; this is true of CarValet. To edit a car, you need to show and change values. UILabel can only display values. UITextField enables the user to edit text. Anything that can be changed to and from text can be edited, including strings, numbers, and dates. A good rule of thumb is that any text string or non-object value stringWithFormat: can create is editable with UITextField.

Like UILabel, UITextField uses the text attribute for what is displayed. You can use it to display a string—say, the current make of a car—as well as read whatever changes the user made. Text fields come with lots of handy behaviors, including support for keyboards, copying, pasting, undoing, and even placeholder strings that suggest what to type.

When you design editing screens, part of the design involves how an object is edited. This might seem strange at first, but mobile devices, especially handheld ones, don’t have an abundance of room. As you move through the book, you learn different ways to edit, but for now, you can present all the fields on one screen.

You need to add nine more visual elements to the project: one for the Car Number label, and two for each attribute so your scene looks like the one in Figure 3-12. To get some practice, try doing this before you continue reading.

Image

Figure 3-12 Scene 2 visual elements

As you tried adding the visual elements, you might have found yourself following these steps in the Storyboard editor for CarEditViewController:

1. Drag out a UILabel for the car number. Set the text to Car Number: 999 and set the font to Headline.

2. Drag out a UITextField on the right-hand side of the view.

3. With the text field selected, show the Attributes inspector and look for the Clear Button list and set it to Appears While Editing. The x clear button then shows in the active field if there is any content.

If you are doing this step after adding all the text fields, select all the text fields. You can do this by holding the Cmd key and tapping on each view.

4. Drag out the UILabel for Make: on the left-hand side.

5. Right-align the label. Again, if you are doing this after initially laying out all the views, select the four labels next to the text fields and then change the alignment.

6. Adjust the label and text field so they are vertically centered in relation to each other. Make sure there is vertical separation between them and the Car Number label.

7. Select the label and field you just created and duplicate them (by selecting Edit > Duplicate the menu or pressing Cmd-D).

8. Move the new label and field combination down until IB indicates the correct vertical spacing. Now make sure the label and edit fields are vertically centered with the corresponding label and edit fields above. Change the label to Model:.

9. Duplicate the line two more times to make the Year: and Fuel: lines.

10. Adjust the spacing to make the view look less crowded. Figure 3-12 uses 25 points of vertical space between text fields.


Note: Why Make the Labels Less Crowded?

In step 10, you adjust the labels to look less crowded. Why do you need to do this? It is important for two reasons. First, when items are too close together, it is hard for the user to easily differentiate what they are. This happens because of how the eye and brain work together to interpret things. Look at a very crowded picture and try to find something. It is difficult, which is why the “find these items in this picture” puzzles are challenging.

When things are difficult to differentiate, the user gets frustrated and might pick a different app. Apple has a set of recommendations for minimum distances. IB supports these by “snapping” objects to dashed lines that show up as you move objects closer together. These distances are minimums.

The other reason for adjusting labels so they don’t look crowded is that fingers are big. A cursor on a laptop or desktop is very small and can easily hit targets that are 5 to 10 points square. Fingers are much larger, so Apple recommends that targets be much larger. For example, the minimum recommended view button size is 44×44 points, with 8 points between adjacent ones.

As you design your interfaces, pay attention to visual separation. It is one thing that differentiates well-designed, easy-to-use apps from all the rest.


The combination of a label and the adjacent text field is very common. Text fields are taller than labels, so the rule of thumb is to first add the field and then the label. Also, the text field is used to set vertical spacing.

You can make it easier to check your changes by setting the initial scene shown by the app to the editor:

1. Open Main_iPhone.storyboard.

2. Find the gray starting arrow next to the original view controller scene. This is the arrow shown in Figure 3-1.

3. Click and drag the arrow so it points to the new car edit scene.

4. When the new scene highlights, release the arrow. It is now pointing to the editing scene.

Run the app and see how the screen looks. If it seems unbalanced or crowded, use the editor to adjust the spacing. This cycle of running and adjusting is very common in development. UI designers (sometimes you) regularly do this to tweak position, graphics, colors, and font sizes.

Adding Editor Behaviors

You need a reference to each field so you can populate and read the text. As before, you add the behaviors in two steps: First, you add the outlets and actions, and then you implement the behaviors. You use Table 3-3 for setting the outlets and actions. As before, you want to open the storyboard with the Assistant editor showing CarEditViewController.h.

Image

Table 3-3 CarEditViewController.h Outlets

To set values for the Car Number label and the text fields, you need a reference to the car object being edited as well as the car number. The add/view scene needs to communicate data to the edit scene. The first approach is to add public properties to the edit view controller class.

Modify CarEditViewController.h so it looks like Listing 3-7. The new code is in bold.

Listing 3-7 Public Properties for Edited Car


//  CarEditViewController.h
//  CarValetScenes

#import <UIKit/UIKit.h>

@class Car;                                                     // 1

@interface CarEditViewController : UIViewController

@property (nonatomic) NSInteger carNumber;
@property (strong, nonatomic) Car *currentCar;                  // 2

@property (weak, nonatomic) IBOutlet UILabel *carNumberLabel;
@property (weak, nonatomic) IBOutlet UITextField *makeField;
@property (weak, nonatomic) IBOutlet UITextField *modelField;
@property (weak, nonatomic) IBOutlet UITextField *yearField;
@property (weak, nonatomic) IBOutlet UITextField *fuelField;

@end


Here’s what happens in the numbered lines in Listing 3-7:

1. @class specifies a forward reference. See the “Forward References” sidebar.

2. The controller uses the strong memory qualifier as it needs to make sure the edited car is not removed from memory.


Forward References

Before you can declare the type of an instance variable or property, the compiler needs to know about that type. A view controller header file already imports the common iOS header files so you have access to all those class definitions.

There are two ways to use a new class in a declaration. The first is to import the header file for the new class type. To create a Car property, you would #import "Car.h".

Another way is to declare a forward reference to the Car class. @class Car; tells the compiler that Car is a valid class and will be defined elsewhere. Typically, you use @class in an .h file and #import in an .m file.

There are three reasons to use forward references. The first is to speed up building your code. In the small projects you have done so far, there is really no issue. But in larger projects with trees of subclasses, Xcode has to process each imported file in a header. It is possible to have many imported classes that are processed for each header file, even though there are only one or two implementation files that need the class.

The second, and less common, reason is to avoid circular references. This happens when two classes end up referring to an instance of each other. That is, ClassA needs to include a property of type ClassB, and ClassB has a property of type ClassA. Usually, the problem is not so obvious, and the properties are declared in subclasses of other classes.

The final reason is to provide flexibility and reusability. You read more on this in the section “Exchanging Data Using a Protocol.”

The safest practice is to use @class in .h files and #import in .m files.


Add code to CarEditViewController.m to import Car.h. Then use Listing 3-8 to change viewDidLoad: to set the text of the Car Number label and populate the data fields.

Listing 3-8 CarEditViewController.m viewDidLoad:


- (void)viewDidLoad {
    [super viewDidLoad];

    NSString *carNumberText;
    carNumberText = [NSString stringWithFormat:@"Car Number: %d",
                     self.carNumber];
    self.carNumberLabel.text = carNumberText;

    self.makeField.text = self.currentCar.make;                              // 1
    self.modelField.text = self.currentCar.model;
    self.yearField.text = [NSString stringWithFormat:@"%d",                  // 2
                           self.currentCar.year];
    self.fuelField.text = [NSString stringWithFormat:@"%0.2f",               // 3
                                    self.currentCar.fuelAmount];
}


Here’s what happens in the numbered lines in Listing 3-8:

1. make and model are both string-based attributes, so they can be set directly.

2. Use a formatted string for the integer value representing the year.

3. Because fuel is a float, using the format code @"%0.2f" limits the resulting string to two decimal places. (As you see in Chapter 5, “Localization,” this is not a localization safe way of formatting a number.)

The goal of the scene is for the valet to edit a car. This means updating the properties of the Car object. For now, it happens when the view disappears. Listing 3-9 shows viewWillDisappear:, which is a method that gets called each time the view disappears. Add that method belowviewDidLoad. The implementation reads the text fields to update currentCar.

Listing 3-9 CarEditViewController.m viewWillDisappear:


- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    self.currentCar.make = self.makeField.text;                            // 1
    self.currentCar.model = self.modelField.text;
    self.currentCar.year = [self.yearField.text integerValue];             // 2
    self.currentCar.fuelAmount = [self.fuelField.text floatValue];         // 3
}


Here’s what happens in the numbered lines in Listing 3-9:

1. make and model are strings, so you can set them directly.

2. Set year by converting the contents of the year field to an integer value.

3. Set fuelAmount by converting the contents of the fuel field to a float value.

When you run the code, the fields are set but the data is wrong. Number fields are set to 0, and strings are empty. This is because the default value for carNumber is 0, and currentCar is nil. Remember that accessing a property really calls a method. Sending a message to a nil object does not result in an error. Instead, it returns nil. The text field interprets an argument of nil as an empty string. The number formats show the value for the memory address value for nil, 0x00000000.

Tap in each of the fields. iOS automatically brings up the default keyboard. Part of a good user experience is using the right element for the right job. A text field can show several different types of keyboards. To help the user enter numbers, change the type of keyboard for year and fuel:

1. Open the Storyboard editor and select the Year field.

2. Make sure the Attributes inspector is open and then select Number Pad from the Keyboard popup shown in Figure 3-13.

Image

Figure 3-13 Setting the number pad keyboard

3. Click on the Fuel field and select Decimal Pad.

As you set the keyboards, note the other kinds provided by the system, including ones for e-mail addresses and URLs. As you create apps, it is important to choose the right type of keyboard for two reasons. First, it enables the user to quickly and easily enter data. Second, certain keyboards can help reduce errors. For example, a number keyboard means the user is unable to accidentally type in alpha characters.

Try the app again. First, tap in the Make field and then the Year field. The keyboard changes automatically for each field.

Hooking It All Together

You have laid out a new scene, set up the connections, and added behaviors. Now you need to add the edit scene into the flow of the app. Hooking scenes together is a common task in development. As you will see, it is more than just pointing from one to another. There can be data sharing or other types of behaviors associated with movement. The first step in hooking it all together is to add a button to the add/view scene that transitions to the edit scene:

1. Open the Storyboard editor and move the gray first scene arrow back to the add/view scene.

2. Drag out a button and set the title to Edit. The best way to do this is double-clicking the “Button” text on the storyboard layout view to select the title text, and then typing in the new title. When you do this, Xcode automatically resizes the button element to best fit the new text.

3. Center the button vertically between the Previous and Next buttons. Do this by dragging the button until you see both the vertical center and horizontal alignment guides, as shown in Figure 3-14.

Image

Figure 3-14 Aligning the Edit car button

In apps with more scenes, keeping track of the current scene, where the user came from, and the path back is very important and can be very complex. Luckily, iOS provides navigation controllers to manage the transitions. You learn a lot more about different controllers in Chapters 7, “Navigation Controllers I: Hierarchies and Tabs,” and 11, “Navigation Controllers II: Split View and the iPad.”

Carry out the following steps to add a UINavigationController (see Figures 3-15 and 3-16).

1. Open the storyboard and select Scene 1. One way to quickly do this is to click in the black object bar below the scene.

2. Embed the scene in a navigation controller. At the time this book is being written, this is done by selecting Editor > Embed In > Navigation Controller, as shown in Figure 3-15.

Image

Figure 3-15 The Embed In menu

Image

Figure 3-16 The new navigation controller

After embedding the new navigation controller, the first two controllers on the storyboard look like Figure 3-16. There are three important changes to note here:

1. Xcode moves the first screen arrow to the inserted navigation controller.

2. A relationship link is added between the navigation controller and the add/view scene.

3. The navigation controller adds a title bar to the scene.

Double-click in the title bar of the add/view scene to set the title to CarValet. Now run the app and notice how the title bar obscures the Total Cars label and part of the New Car button, as shown in the left side of Figure 3-17. The label and button are still there, and, if you look carefully at the title bar, you see the label underneath. In iOS 7, the title bar is slightly transparent, providing the users context as to where they are in the app.

Image

Figure 3-17 Scene 1 before and after

Move all the view elements down, as the view does not scroll. You want to do that in roughly four steps:

1. Move the top of the car info view down.

2. Select the view elements above the car info view: the Car Number label, divider, New Car button, and Total Cars label. You can do this by pressing and holding the mouse below the Car Number label and dragging up to the top of the view, as shown in Figure 3-18.

Image

Figure 3-18 Selecting the view elements under the title bar

3. Move these elements down until the top of the Total Cars label is about 8 points below the title bar.

4. Now adjust the top of the Car Info label so it is the default distance from the bottom of the Car Number label. The final layout will look like the right side of Figure 3-17.

Transitions

Apps usually show standard graphical transitions when moving from one scene to another. iOS provides full support for those transitions.

Add a transition between the add/view scene and the edit scene when the valet taps the Edit button. To create the transition, Ctrl-drag from the Edit button to the edit scene. Choose Push from the popup that appears, as shown in Figure 3-19. Note that the title of this popup title is Action Segue. Segues are the objects that implement transitions. Figure 3-20 shows an arrow between the two scenes. That is how storyboards show segues. In code, they are represented by an instance of the UIStoryboardSegue class.

Image

Figure 3-19 The action segue popup

When you connect the scenes, Xcode automatically embeds the edit scene in the top-level navigation controller. This is why a title bar is added to the edit scene. Set the title of the scene to Edit. Also move all the contents of the view, the Car Number label and label/edit field pairs, down below the title bar.

Run the app and tap the Edit button. The navigation controller brings the edit scene on from the left. It also adds a Back button to the title bar. Tap that to go back to the add/view scene. You learn more about the navigation bar, including how to add your own buttons, in Chapter 7.

Before you continue, update the views in the car edit scene so they are below the navigation bar.

The Magic Method: prepareForSegue:sender:

When you transition from one scene to another, there is usually some sort of transfer of information. The user is going somewhere to do something or view something. In the case of the CarValet app, the valet is editing and updating car data, but the technique described next also applies to many other apps.

You need to get the information to the new scene after the segue has been started but before the new scene gets to viewDidLoad:. This is exactly what prepareForSegue:sender: is used for. It is called after each segue is initiated but before the results of that segue occur. The most important thing to remember is that prepareForSegue:sender: is sent to the view controller currently on the screen, not the incoming one.

To pass a car, set the carNumber and currentCar properties in the incoming CarEditViewController and make the following changes to ViewController.m:

1. Import CarEditViewController.h.

2. Add the code shown in Listing 3-10 to ViewController.m, just above viewDidLoad.

Listing 3-10 ViewController.m prepareForSegue:sender:


- (void)prepareForSegue:(UIStoryboardSegue *)segue
                   sender:(id)sender {
    if ([segue.identifier isEqualToString:@"EditSegue"]) {            // 1
        CarEditViewController *nextController;

        nextController = segue.destinationViewController;             // 2
        nextController.carNumber = displayedCarIndex + 1;             // 3

        Car *currentCar = arrayOfCars[displayedCarIndex];             // 4
        nextController.currentCar = currentCar;                       // 5
    }
}


Here’s what happens in the numbered lines in Listing 3-10:

1. Set up the edit scene only if this is the edit segue.

2. Get the edit scene view controller from the segue. A segue has properties for the source and destination controllers. That is, for the controller currently on the screen (source) and the incoming one (destination).

3. Set the car number property, remembering that the index is 0-based.

4. Use the Objective-C subscripting instead of the objectAtIndex: message.

5. Find and set the right Car object.

To call the code inside the if statement, you need to set the identifier of the segue. To do that, open the Storyboard editor, select the segue that connects to the edit scene, and open the Attributes inspector. The segue is the arrow with the round type identifier shown between the two scenes inFigure 3-20. Set Identifier to "EditSegue", the same string used in Listing 3-10.

Image

Figure 3-20 The segue connection

Run the app, tap Edit, and then tap into the Make field. You see a screen like the one shown in Figure 3-21. Type in a make, model, and year and then tap the CarValet app’s Back button.

Image

Figure 3-21 Editing a car

Nothing changes. You might think things are broken, but actually, the car is saved. To see this, add another car, tap Next, and then tap Previous. The car detail screen shows the changes you made.

Before adding code to update the add/view display after an edit, there is a slight formatting issue with the edit scene. The lines with labels and edit fields appear to be off center. To make the view look balanced, line up the colons of the labels with the colon in the Car Number label. Good-looking apps are appealing partly because they show things lined up. A quick way to do this is the Guides feature in Interface Builder. With it, you create horizontal and/or vertical guides for aligning visual elements. Using Guides in combination with Snap to Guides (which you access by selecting Editor > Canvas > Snap to Guides) makes aligning views easy.

When a view is selected, you create a horizontal guide by selecting Editor > Add Horizontal Guide, or Cmd-Shift-hyphen. For a vertical guide, use Editor > Add Vertical Guide or Cmd-Shift-backslash. Drag the new guide to the desired position, using the displayed point location: a number displayed on each side of the guide, one showing the distance from the top or left, and the other showing the distance from the bottom or right. Figure 3-22 shows a guide with numbers. When you are done, drag the guide out of the view.

Image

Figure 3-22 Using a vertical guide for layout


Note: Why Are Add Horizontal/Vertical Guides Disabled?

Adding a guide requires selecting a view. If the menu items are disabled, it is because you do not have an appropriate view selected. The easiest way to do this is to select the correct view in the left-hand column of view elements in the Storyboard editor.


Why Not Segue Back?

Your first thought for telling the add/view scene to update may be to segue back. Unfortunately, this does not work. Each segue creates a new view controller. Instead of going back to the same add/view view controller instance that opened the edit scene, you would go to an entirely new view controller with just one car. Even worse, the original edit scene would not be disposed of, and each edit/return cycle would create two more controllers that take up memory.

You can update the add/view scene in at least three ways:

Image Add a currentCarUpdated method to ViewController and a link to the ViewController object in CarEditViewController. Call the update method when the edit scene disappears.

Image Use a protocol to tell ViewController to update.

Image Use the unwind segue mechanism that first appeared in iOS 6.

The first option works but also significantly reduces code reusability and modularity. Each view controller object needs to know too much about the other, so they cannot be separated. Using protocols eliminates this problem, as discussed in the following section.

Improving the Storyboard: Take 1

Most apps have some need to exchange data with and/or trigger behaviors in other objects. Doing so can lead to interdependent code that is hard to maintain and reuse. Instead, you can use protocols.

Protocols increase the maintainability, reusability, and flexibility of your code. You can think of a protocol as a contract between a requestor and a provider. The provider, or delegate as it is usually called, agrees to implement a set of methods that the requestor can call. Each method is usually used for one of three purposes:

Image To request data from the delegate, such as a Car object

Image To inform the delegate of a change in state that enables saves, cancels, and/or redisplays data

Image To perform a specific behavior, such as paging someone to fetch the car

The main advantage of protocols is reusability. Any class can be a delegate, as long as it implements the required methods. Similarly, any class can use the protocol methods to request information or initiate behaviors.

For more information on protocols, see the section on protocols on page 231 in Programming in Objective-C 2.0, 2nd edition, by Stephen G. Kochan, published by Addison Wesley. Alternatively, read the section on protocols in Apple’s Objective-C documentation.


How Protocols Help

One of the advantages of object-oriented programming is encapsulation. You take the behaviors that make up your application, divide them out into components that interact together, and create objects for those components.

Some components might be pieces of the interface or views. Others could be parts of the underlying data or model. And still others, controllers, hook the two together. But those divisions are not quite enough. As you create more applications, you find yourself doing the same kinds of things over and over again.

One example is a series of related screens: One shows a set of data objects, the cars; another shows the detail for just one object, a car detail view; and another lets you edit attributes of that car, a make or year editor.

The typical first-time way to solve this common pattern is to hand around references to both the object and any relevant screens. The detail screen gets a reference to the overview, the Attribute editor gets a reference to the detail screen, and so on.

Then each class implements methods for other classes to call. The overview has a method for updating a car, as does the detail view. The detail view has a method for updating fields. Soon you end up with a spaghetti bowl of interrelated methods, attributes, and screens. A change in one place can have significant impacts on others. The code is not flexible, and it is difficult to maintain.

Even worse, the classes become specific to the type of data they show, the set of classes used, and the interface flows. They are not reusable.

Using protocols is one way to fix that. A protocol defines how one kind of object talks to another: What information do I want? What information and status will I send? and What actions can I perform? Protocols can also specify which messages a delegate must support and which are optional.

Using protocols allows you to untangle the spaghetti. Changes are easy to make because you know exactly how one class, screen, or object talks to another one. You can change the entire implementation code for the provider or user of the protocol, as long as they both conform to the protocol. And as you progress, you start to abstract out common types of needs into classes you can use for many projects.

Using protocols makes all this much easier, especially when you realize that an object can be the delegate of, and/or implementer of, more than one protocol.


Exchanging Data Using a Protocol

One common use of protocols is to exchange data. The editor, or requestor, needs information, and the delegate provides and perhaps updates it. In this section, you design a protocol.

In this case, your car editor needs to know both the car object and number. The add/view scene, or delegate, needs to be informed when the car is changed so you can update the display. That makes three methods for the protocol:

Image carToEdit returns the car object to edit.

Image carNumber returns the number (not the index) of the edited car.

Image editedCarUpdated tells the delegate that editing is done.

Now create a new Objective-C protocol called CarEditViewControllerProtocol and add it to the project. Set the content of the file to Listing 3-11. Creating a protocol file is just like creating a class, except you choose the “Objective-C protocol” file template.

Listing 3-11 CarEditViewControllerProtocol.h


//  CarEditViewControllerProtocol.h
//  CarValetScenes

#import <Foundation/Foundation.h>                             // 1

@class Car;

@protocol CarEditViewControllerProtocol <NSObject>            // 2

    - (Car*)carToEdit;                                        // 3

    - (NSInteger)carNumber;

    - (void)editedCarUpdated;

@end


Here’s what happens in the numbered lines in Listing 3-11:

1. Import any headers for types used in the protocol. Foundation.h defines many of the core Cocoa types, including NSInteger.

2. @protocol is the directive for declaring a protocol. The next part is the protocol name, followed by any included protocols. In this case, CarEditViewControllerProtocol has access to any methods declared in the NSObject protocol, such as self, class, anddescription.

3. The method declarations are specified the same way as public class methods.

A forward reference is used for the Car object. Protocols are some of the most common places to use the @class directive. One reason is to prevent circular references. The other is to increase flexibility and reusability. The protocol does not care about the properties or methods of Car. This means you can use the protocol in another project that has a completely different Car class. All that matters is the name of the class.

When your protocol is defined, change the requestor to use it. For CarValet, start by changing the include file. In CarEditViewController.h, remove the carNumber property and then make the changes shown in bold in Listing 3-12.

Listing 3-12 Protocol Changes to CarEditViewController.h


#import <UIKit/UIKit.h>

#import "CarEditViewControllerProtocol.h"

@class Car;

@interface CarEditViewController : UIViewController

@property (weak, nonatomic) id <CarEditViewControllerProtocol> delegate;

@property (strong, nonatomic) Car *currentCar;


The second-to-last line in Listing 3-12 is an important part of the protocol mechanism. id is a placeholder reference for any class of object. This means that any class, including ones you have not even thought of yet, can be a delegate for the protocol. Although the edit scene requires Car to have certain properties, you still have lots of flexibility in how the object is implemented. You can reuse the editor scene in other projects. Also note that you do not use an asterisk when declaring a variable of type id. The declaration is supposed to be delegate, not *delegate.

Using angle brackets around CarEditViewControllerProtocol after the type indicates that whatever the class of delegate, it must conform to the protocol. That is, it must implement all the required methods of the protocol.

Now update viewDidLoad in CarEditViewController.m to match Listing 3-13. The changes are in bold. The class has access to the protocol because it is included in the corresponding .h file.

Listing 3-13 CarEditViewController.m Modified viewDidLoad:


- (void)viewDidLoad {
    [super viewDidLoad];

    NSString *carNumberText;
    carNumberText = [NSString stringWithFormat:@"Car Number: %d",
                                [self.delegate carNumber]];                  // 1
    self.carNumberLabel.text = carNumberText;

    self.currentCar = [self.delegate carToEdit];                             // 2
    self.makeField.text = currentCar.make;
    self.modelField.text = currentCar.model;
    self.yearField.text = [NSString stringWithFormat:@"%d",
                                    currentCar.year];
    self.fuelField.text = [NSString stringWithFormat:@"%0.2f",
                                    currentCar.fuelAmount];
}


Here’s what happens in the numbered lines in Listing 3-13:

1. The car number is now returned from the delegate.

2. Call the delegate for the edited Car object.

The final modification tells the add/view scene to update itself. You add a call to [self.delegate editedCarUpdated] at the end of viewWillDisappear:.

Modifying the ViewController Class

Now that you have finished the requestor, it is time to update the delegate.

CarEditViewController now expects all data to be communicated through the protocol. It also lets the delegate know when editing has finished. You need to enable ViewController to be a delegate for the protocol.

Modify the top of ViewController.h by adding the bold code from Listing 3-14.

Listing 3-14 Adding the Protocol to ViewController.h


#import <UIKit/UIKit.h>

#import "CarEditViewControllerProtocol.h"

@interface ViewController : UIViewController
<CarEditViewControllerProtocol>



<CarEditViewControllerProtocol> says that ViewController supports—that is, conforms to—your protocol. Of course, you have not yet implemented any support. And this shows one of the ways Xcode helps you check that your code is complete.

Look in the status area in the center of the Toolbar (refer to area 3 in Figure 2-5), and you see a yellow warning triangle on the right side. This says that compiling your code causes warnings. Now open ViewController.m and look at the @implementation line. Xcode shows a warning. Click on the warning to find more details. It shows an incomplete implementation error because the protocol methods are missing.

Add the protocol methods shown in Listing 3-15 just above newCar:.

Listing 3-15 Implementation of CarEditViewControllerProtocol


- (Car*)carToEdit {
    return arrayOfCars[displayedCarIndex];                   // 1
}

- (NSInteger)carNumber {
    return displayedCarIndex + 1;                            // 2
}

- (void)editedCarUpdated {
    [self displayCurrentCarInfo];                            // 3
}


Here’s what happens in the numbered lines in Listing 3-15:

1. Edit the car currently displayed to the valet.

2. Make the number to display one larger than the index.

3. When the car information changes, update the displayed information.

The final step is to set ViewController as the protocol delegate. The best place to do this is prepareForSegue:sender:, as it is called early enough in the transition. Change the body of the method to match Listing 3-16.

Listing 3-16 The New prepareForSegue:sender:


if ([segue.identifier isEqualToString:@"EditSegue"]) {
    CarEditViewController *nextController;

    nextController = segue.destinationViewController;
    nextController.delegate = self;
}


This time, running the app works as expected. The valet can add cars, edit them, and see the changes when returning to the add/view scene.

Improving the Storyboard: Take 2

iOS 6 introduced a better way to return data to the view controller that initiated the segue: You simply add a special type of IBAction to your view controller. Usually an action method has an argument that looks like (id)sender, or it has no argument at all. By changing the argument to(UIStoryboard*)segue, you create a special type of action, one that can accept segues.

As you saw earlier in this chapter, prepareForSegue:sender: is sent only when one scene initiates a transition to another. It is a forward segue. This other kind of action is the opposite: It is sent when a scene wants to go back to the previous one. It unwinds the segue that first opened the scene, and it opens the previous scene without creating a new one.

To see this in action, add the code in Listing 3-17 to the bottom of ViewController.m.

Listing 3-17 Unwind Segue Action for ViewController.m


- (IBAction)editingDone:(UIStoryboardSegue*)segue {
    [self displayCurrentCarInfo];
}


Now you need to trigger the unwind action. In the storyboard, add a Done bar button item to the title bar in your car edit scene, as shown in Figure 3-23. Navigation bar buttons are smaller versions of their cousins, and there are many different kinds provided by the system.

Image

Figure 3-23 Adding a Done bar button item

Use the Attributes inspector to set the bar button item style to Done, and the identifier Done. As you are changing the identifier, look at the Identifier list for the many types you can add to future apps.

Now Ctrl-drag from the bar button to the exit segue item in the bar below the scene. Xcode dynamically generates a list of possible exit segues by looking in scenes that could have opened the current scene. Any unwind segues—that is, any IBAction method with aUIStoryboardSegue argument—are shown as possible targets for the exit. Your project has only one originating scene, but others can have complex trees of scenes.

Figure 3-24 shows the Action Segue popup with editingDone: selected.

Image

Figure 3-24 The exit segue

To prove that this works, add the following line to the end of editCarUpdated in ViewController.m:

NSLog(@"\neditedCarUpdated called!\n");

This causes editedCarUpdated called! to print to the console.


Pro Tip: What Is "\n"?

The NSLog statement has a weird-looking string (shown in bold): "\neditedCarUpdated called!\n". In addition to the information printed out in the console, there are a couple of two character sequences: "\n". Those are escape sequences, special characters you can use for a non-printable character, in this case, a newline. That is why the message shows up on a different line from the date and time of the log item. Using newlines can be a handy way to provide visual separation for items.


Run the project and try these two things:

1. Edit a car and touch the Back button. The console should show something like this:

2013-03-16 20:28:14.115 CarValetScenes[45542:c07]
editedCarUpdated called!

2. Edit a car, make some changes, and touch Done. The edit scene slides out, but the car does not appear to be updated. Move to the previous or next car and then move back to the just edited car. The displayed information is now correct.

The unwind action is sent before the car is updated in the viewWillDisappear: method of CarEditViewController. Once again, prepareForSegue:sender: is the magic method. Make the following changes so the displayed car detail is updated when returning to the add/view scene:

1. Open the Storyboard editor and select the Unwind Segue from Done to Exit from the list of scene objects on the left. The segue is shown selected on the left side of Figure 3-25.

Image

Figure 3-25 Setting the exit segue identifier

2. Use the Attributes inspector on the right to give the segue the identifier EditDoneSegue as shown in Figure 3-25.

3. Add the prepareForSegue:sender: shown in Listing 3-18 to CarEditViewController.m. The car object update code is identical to that in viewWillDisappear:.

Listing 3-18 CarEditViewController.m prepareForSegue:sender:


- (void)prepareForSegue:(UIStoryboardSegue *)segue
                   sender:(id)sender {
    if ([segue.identifier isEqualToString:@"EditDoneSegue"]) {
        self.currentCar.make = self.makeField.text;
        self.currentCar.model = self.modelField.text;
        self.currentCar.year = [self.yearField.text integerValue];
        self.currentCar.fuelAmount = [self.fuelField.text floatValue];
    }
}


After checking for the right segue, the method updates the values in the car object. There is no need to call the editingDone: action, as that is taken care of by the segue mechanism. The Back button continues to work, using viewWillDisappear:animated:.

Now when you run the app and touch Done, everything works as expected.

Summary

In this chapter, you have used storyboards to create an application UI. You have created individual scenes and the transitions between them. You have also learned different ways to share data between scenes, which is something you will do in almost every application you write.

Using the Storyboard editor makes it easy to create and manage the many scenes that make up a typical application. You can change the flow of scenes, and you can zoom out to see an entire application at a glance.

As you worked through the chapter, you practiced using other useful development skills and tools, including code consolidation, forward references, protocols, and using guides to help layout and align view elements. You also learned about UITextField, a class you will use many times as a simple way to display and update content.

The interface in this chapter is designed for a 4-inch retina display. There is also a 3.5-inch display, as well as portrait and landscape in both display sizes. In Chapter 4, “Auto Layout,” you learn a more powerful way to lay out your interfaces once so that they automatically adjust to many different screen sizes and orientations.

Challenges

1. Add an About This app scene. It can be triggered from an About button in the add/view scene or, for a more advanced challenge, an Info button in the navigation bar.

2. Modify the project so that it uses only the segue and unwind segue mechanisms to share data and updates. Hint: Start by removing the changes made to add the CarEditViewControllerProtocol.

3. Add behavior to the detail area of the add/view scene that disables the Previous and Next car buttons when there is no previous or next item. Hint: UIButton inherits from UIControlItem that has a BOOL property called enabled. YES enables the button, NO disables it.

4. Add the capability to cancel editing of a car. Hint: You need to have UI elements for saving and canceling as well as associated behaviors. It is possible to replace the Back button. It is also possible to have multiple segues with different names.

5. Update the property declarations in Car.h to include default properties.