iOS App Development For Dummies (2014)

Part II. Building RoadTrip

Chapter 5. Creating the RoadTrip User Interface

In This Chapter

arrow Seeing how storyboards work

arrow Working in the Utility area

arrow Understanding and adding navigation controllers

arrow Using Interface Builder to add objects to your storyboard

If you’ve read the preceding chapters, you have the foundation for understanding the tools you need to build an app, with particular focus on the example app developed in this book. Now you’re ready to find out how to add a user interface to your app via the storyboard.

In this chapter, I show you how to add items to the TestDriveController’s view using both Interface Builder and the user interface objects available to you in the Library pane in the Utility area. You first add these items to your iPad storyboard and then add similar items to your iPhone storyboard.

Creating Your User Interface in the iPad Storyboard

In the Project navigator, select the Main_iPad.storyboard file and you’ll see several view controllers in the Document Outline (Split View Controller, Master View Controller, and Detail View Controller). Each view controller in a storyboard file manages a single scene (a scene, in this sense, is really just a view controller). On the iPad, courtesy of the Split View Controller (which I explain in more detail in Chapter 13) or a popover controller, you can have multiple scenes on a screen. (On the iPhone, you can generally see only one scene on a screen at a time.) In the Document Outline, select the disclosure triangle next to the Master View Controller in the Master View Controller – Master Scene to expand the view controller and you’ll see its view.

image If you can’t see the Document Outline, you can use the Hide/Show Document Outline control shown in the lower-middle of Figure 5-1. You can also zoom in and out of a storyboard by double-clicking in the storyboard’s Canvas, or by using the zoom control shown in the lower-right in Figure 5-1. The = sign returns the storyboard to full size, which is the only way views are editable.

To add user interface elements, select the view you want to work with under the view controller heading listed in the Document Outline.

It’s about the view controller

Selecting a storyboard file in the Project navigator launches Interface Builder, which is the editor you use to edit the storyboard files for your application. Most applications need only one storyboard file, but because you're creating a universal app, you’ll have two storyboards, one for the iPad user interface (Main_iPad.storyboard) and one for the iPhone user interface (Main_iPhone.storyboard). Each storyboard file you create has its own initial view controller, which serves as the entry point into the storyboard. In your application’s main storyboard file, the initial view controller would be the first view controller presented by your application.

image

Figure 5-1: The initial Main_iPad storyboard.

The view controller is the big kahuna here, and each view controller in a storyboard file, as I said, manages a single scene. For iPhone applications, a scene manages one screen’s worth of content, but for iPad applications, the content from multiple scenes can be onscreen simultaneously. To add new scenes to your storyboard file, all you have to do is drag a view controller from the Library to the storyboard canvas. You can then add controls and other views (such as Image, Web, or even Table views) to the view controller’s view.

image Besides the ability to lay out your application as a whole, storyboards also reduce the amount of code you have to write. Say you want to create a transition from one view controller to another; all you would need to do is Control-click a button or Table View cell in one view controller and drag to the other. Dragging between view controllers creates a segue, which appears in Interface Builder as a configurable object. Segues support all the same types of transitions available in UIKit, such as navigation and modal transitions. A segue also enables you to define custom transitions.

I explain more about segues and view controller transitions in Chapter 14, when you add more scenes and segues to the RoadTrip app.

Using Interface Builder to add the user elements

Xcode’s Interface Builder enables you to create a storyboard by letting you lay out your user interface graphically in each view controller. You use Interface Builder to design your app’s user interface and then save what you’ve done as a resource file, which is included in your app and loaded into your app at runtime. This resource file is then used to automatically create the window and your app’s view controllers, as well as all your views and controls.

image If you don’t want to use Interface Builder, you can also create your objects programmatically — creating views and view controllers and even things like buttons and labels using your very own application code. I show you an example of creating a button programmatically in Chapter 15.

So how do you actually get those little controls into the view that lives in the view controller scene? For that, you use another area of the workspace — the Utility area.

image You use the View selector on the toolbar to display or hide the Utility area. The Utility area is an optional area on the right side of the Workspace window. To hide or show the Utility area, click the Utility button on the View selector on the right on the Workspace toolbar. (In Figure 5-2, I’m using the View selector to open the Utility area.)

image When you hover your mouse pointer over a toolbar button, a tooltip describes its function.

image

Figure 5-2: The View selector.

image Figure 5-3 shows the Utility area in all its glory. I have resized the Library pane.

image

Figure 5-3: The Utility area.

As you can see, this area includes two panes, the top one for Quick Help and other inspectors (the Attributes inspector is selected here), and the bottom one for libraries of resources.

Working within the Utility Area

The Utility area consists of the Inspector and Library panes and their corresponding Inspector and Library selector bars. The idea is to use the Inspector pane to view and access Quick Help and other inspectors, and to scour the Library pane for ready-made resources you want to use in your project. You’ll be using both the Inspector and Library panes in this chapter.

Inspector and Quick Help pane

You use the Inspector selector (shown in Figure 5-4) to toggle on the particular inspector you want to use. (Xcode makes the decision-making process a bit easier by having your choice of Navigator or Content editor predetermine which inspectors in fact show up in the Inspector selector.)

image

Figure 5-4: The Inspector selector.

Utility area inspectors perform a variety of tasks. Following is a list of important inspectors and what you use them for:

·        image File inspector (first button): Lets you view and manage file metadata such as its name, type, and path.

·        image Quick Help (second button): Lets you view applicable details about what has been selected in an editor. Details include an abstract or concise description, where and how the selected element is declared, its scope, the parameters it takes, its platform and architecture availability, references, sample code, and so on. Different editors support different elements for selection, as follows:

·        Symbols, available for selection in the Source editor

·        Interface objects, available for selection in Interface Builder

·        Build settings, available for selection in the Project editor

Additional inspectors are available in some editors; for example, Interface Builder offers the following:

·        image Identity inspector: Lets you view and manage object metadata such as its class, runtime attributes, label, and so forth.

·        image Attributes inspector: Lets you configure the attributes specific to the selected interface object. For example, some text field attributes include text alignment and color, border type, and editability.

·        image Size inspector: Lets you specify characteristics such as the initial size and position, minimum and maximum sizes, and autosizing rules for an interface object.

·        image Connections inspector: Lets you view the outlets and actions for an interface object, make new connections, and delete existing connections. The Connections inspector is where you’ll find things like outlets and targets that I explain in Chapter 9.

Library pane

A selection in the Library selector in the Library pane does the obvious: It selects a particular library of resources that you can then use in your project. Figure 5-5 shows the choices offered by the Library selector; the following list gives the details:

image

Figure 5-5: The Library selector.

·        image File templates: Click here to find templates for the common types of files you create using the New File menu. To add a file of that type to your project, simply drag it from the File Templates library to the Project navigator.

·        image Code snippets: Need just a smidgeon of code? Click here to find short pieces of source code you can then use in your application. Just drag the bit you found directly into your source code file.

·        image Objects: This library consists of interface objects you can use as part of your user interface. To add one to a particular view, drag it directly into your storyboard or nib file in Interface Builder.

·        image Media files: Here’s where you’ll find graphics, icons, and sound files. To use one, drag it directly to your storyboard or nib file in Interface Builder.

image You can filter out what gets displayed in a selected library by entering your Search text into the text field in the Filter bar at the bottom of the Library pane.

Understanding iPad Navigation

Although the iPhone and iPad are very similar, one area in which they often differ is in how a user can navigate through an application.

For example, in iPhone apps that use a master-detail architecture, a Back button is prominently displayed in a detail view to go back to the Master view. (Figure 5-6 shows what I mean.) An iPad app that uses Split view functionality for the master-detail architecture will not need that Back button. But there are many other user interface designs on the iPad where a Back button is often used.

Apple has built this ability into the iOS architecture and has made it an integral part of the view controller architecture, as personified in the Navigation controller. (Okay, I know it isn't a “person,” but you get the idea.)

image

Figure 5-6: An iPhone application sequence.

A Navigation controller is a Container view controller that enables the user to navigate back and forth between view controllers. A Navigation controller is an instance of the UINavigationController class, which is a class you use “as is” and don't subclass. The methods of this class provide support for managing a stack-based collection of custom view controllers. This stack represents the path taken by the user through the application, with the bottom of the stack reflecting the starting point and the top of the stack reflecting the user’s current position in the application.

image Apple’s UIKit framework (one of the Cocoa Touch frameworks) generally uses class names that begin with UI, such as UIView, UIViewController, UIImageView, UIButton, and many more. To avoid confusion, you should not use the UI prefix for your own class names. Apple also has special prefixes for many other frameworks. For example, the Core Image framework includes classes such as CIColor, CIContext, CIFaceFeature, and so on. These naming conventions provide hints so that when you come across an Apple class named CIImage, you can expect to find it in the Core Image framework.

Some developers adopt their own special prefixes for all their custom classes, including simple schemes such as using the RT prefix, so that class names could be RTMasterViewController, RTMapController, RTWeatherController, and so on. It’s not necessary to use a unique prefix for every custom class name, but you should avoid using Apple’s class names for your own classes.

image A stack is a commonly used data structure that works on the principle of “last in, first out.” Imagine an ideal boarding scenario for an airplane: Passengers would start being seated in the last seat in the last row, and they’d board the plane in back-to-front order until they got to the first seat in the first row, which would contain the seat for the last person to board. When the plane reached its destination, everyone would deplane (is that really a word?) in the reverse order. That last person on — the person in row one, seat one — would be the first person off.

A computer stack works on the same concept. Adding an object is called a push — in this case, when you tap the Travel button, for example, the view controller for that view is pushed onto the stack. Removing an object is called a pop — touching the Back button pops the view controller for the view being displayed. When you pop an object off the stack, it’s always the last one you pushed onto it. The controller that was there before the push is still there and now becomes the active one.

Although the Navigation controller’s primary job is to act as a manager of other view controllers, it also manages a few views. Specifically, it manages a Navigation bar that displays information about the user’s current location in the data hierarchy, a Back button for navigating to previous screens, and any custom controls the current view controller needs.

Take another look at Figure 5-6 and notice that, when the user taps Events in the iPhone version of RoadTrip, the Navigation controller (courtesy of the storyboard) pushes the next view controller onto the stack. The new view controller’s view slides into place and the Navigation bar items are updated appropriately. When the user taps the Back button on the Navigation bar, the current view controller pops off the stack, that view slides off the screen, and the user finds himself back in the previous view.

image The Navigation controller maintains the stack of view controllers, one for each of the views displayed. The very first view controller that the Navigation controller pushes onto its stack when a Navigation controller is created is called the Root view controller. It remains active until the user selects the next view to look at.

Navigation bars enable a user to navigate the hierarchy. Here’s what you need to know in order to make that work:

·        The view beneath the Navigation bar presents the current level of the application.

·        A Navigation bar includes a title for the current view.

·        If the current view is lower in the hierarchy than the top level, a Back button appears on the left side of the bar; the user can tap it to return to the previous level. (Back in Figure 5-6, these buttons are named Road Trip and New York City and are shaped like a left-pointing arrow; the text in the Back button tells the user what the previous level was.)

·        A Navigation bar may also have an Edit button on the right side — used to enter Editing mode for the current view — or even custom buttons.

On the iPad, the Master-Detail Application template has not one, but two Navigation controllers already included in the storyboard — one for the Master View controller and the other for the Detail View controller, as you can see in Figure 5-7.

The only “problem” right now is that each Navigation controller has only one view controller to manage, which means you won’t be able to select anything and see a new view, with its accompanying Back button.

In this chapter, you get to fix that for the Detail View controller, at least.

What you're working toward right now is shown in Figure 5-8. When you tap the first cell in the Master View controller (you’ll add the Test Drive label shortly), a new view controller will slide its view into place. If you select the Back button, you will slide back to the previous Detail view.

image

Figure 5-7: The Navigation controllers are already in.

image

Figure 5-8: Navigating in RoadTrip on the iPad.

image You have other (even slicker) iPad navigation options at your disposal, and I get to them in Chapter 13, where you get a chance to change from navigation that uses the Navigation controller to something a bit more appropriate for the RoadTrip application. For now, though, you’ll go with the Navigation controller approach, just to get you off and running.

So, on your mark, get set, and go. Time to add a new view controller.

Adding a New View Controller

image Your first step in adding a new view controller is to select the iPad storyboard file in the Project navigator. With the storyboard displayed, you then make sure that the Utility area is visible by clicking its icon in the Xcode toolbar’s View selector. With that done, you can now hide the Project navigator by clicking its icon in the Xcode toolbar’s View selector. (See Figure 5-9; remember, the button is a toggle, as I explain in Chapter 2.) Doing so gives you a little more real estate onscreen. (If you are blessed with a large monitor, though, you can keep the Project navigator open.)

Because I have limited space on a book page, I’m hiding the Navigator area, as you can see in Figure 5-10. (When I work on my large-screen monitor, I usually keep the Navigator area shown.)

image As the last step in getting your canvas ready, click the Attributes inspector button in the Inspector selector in the Utility area.

image

Figure 5-9: Hiding the Navigator area.

image

Figure 5-10: Drag in a view controller.

To add the Test Drive controller (which manages the view that will allow you to have RoadTrip’s little car drive up the screen, turn around, and then go back to its original position, all with sound effects), you need to do the following:

1.     Select Objects in the Utility area’s Library pane, and then drag a new view controller from the pane into your storyboard.

image Whereas you can add controls and other views to views only when the storyboard elements are full size, you can add view controllers (and, as you’ll soon see, segues) at any zoom level.

A new scene is created, as shown in Figure 5-10.

2.     Select the Table view in the Master View Controller – Master Scene (as I have in Figure 5-11) and then select the Attributes inspector.

If you look at the Canvas, you see a Table view with Prototype Cells, and a cell with the text of Title.

You’ll notice that, in the Table View section of the Attributes inspector, the Dynamic Prototypes option is selected.

Right now, if you select a cell, nothing happens. That’s because with Dynamic Prototype cells, you have to implement a method in your view controller to do something when a cell is selected (as you will in Chapter 20, where you create cells based on the information for each destination in the Destinations plist).

image

Figure 5-11: The Table view.

For now, all I want to do is to be able to launch a Test Drive controller when the first cell is selected (and I really don’t care about the time stamp). To do that in the most expeditious way possible, I’ll have you use the Attributes inspector to change the Master view from Dynamic Prototypes to Static Cells.

Static cells are used when you know in advance what needs to be displayed in a cell. Instead of having to implement a method in your view controller to return the cell with the text you want, you can format the cells in the storyboard. But more importantly, you can create a segue from a static cell that will launch the Test Drive Controller for you when the cell is tapped.

That’s all I am going to say about static cells for now — I really want to get on with explaining how to create the user interface in the storyboard using Interface Builder, but I promise I’ll return to static cells in detail in Chapter 12, because they are a feature that you’ll probably be using often.

3.     In the Attributes inspector, select Static Cells from the Content drop-down menu, as I have in Figure 5-12.

You’ll notice a change in the Table view. The heading Prototype Cells will disappear and you’ll see three cells each with the text Title.

image

Figure 5-12: Make them static cells.

4.     In the Outline view, expand the disclosure triangle next to the table view and you’ll see a Table View section.

Expand the Table View section and you’ll see three Table View cells. Expand the first Table View cell and you’ll find a content view and then a label. Select the label, and in the Attributes inspector Title field, enter Test Drive as I have in Figure 5-13.

5.     Select the first Table View cell (the cell, not the Test Drive label) in the Document Outline, and Control-drag from the cell in the Master View controller to the view controller you just added, as shown in Figure 5-14. Then release the mouse button.

Control-clicking from a button or Table View cell and dragging to the view controller you want displayed creates a selection segue or an accessory action. When you release the mouse button, you’ll see the Storyboard Segues contextual menu, which pops up onscreen. You’ll learn more about segues in this section and in Chapter 14. We won’t be using an accessory action, but it’s a way to trigger a segue — from an accessory button in a table view cell rather than from a tap anywhere in the cell (a selection segue).

image

Figure 5-13: Your Test Drive cell.

image

Figure 5-14: Drag from the Test Drive cell to the Table view controller.

6.     Select Push from the Selection Segue pop-up menu, as shown in Figure 5-15.

segue performs the visual transition between two view controllers and supports push (navigation), modal, and custom transitions.

push segue changes the scene — and the user sees the new view controller’s view (with its Back button) slide into place when the user taps a button; the Navigation bar items are updated appropriately. (See the “Understanding iPad Navigation” section, earlier in this chapter, for more on adding a Navigation controller.)

In contrast to a push segue, a modal segue presents the view controller modally, with the transition style you specify, and requires the user to do something (tap Save or Cancel, for example) to get back to the previous view controller. As for custom transitions, segues support the standard visual transition styles such as Cover Vertical, Flip Horizontal, Cross Dissolve, and Partial Curl.

image Segue objects are used to prepare for the transition from one view controller to another, which means segue objects contain information about both view controllers involved in a transition. When a segue is triggered — but before the visual transition occurs — the storyboard runtime calls the current view controller’s prepareForSegue:sender: method so that it can pass any needed data to the view controller that's about to be displayed.

image

Figure 5-15: Creating a push segue.

You’ll notice that selecting Push from the Storyboard Segue’s pop-up menu causes the Navigation bar to appear but also shrinks the view. I explain that in Chapter 13.

7.     Select the Push segue in the Master View Controller scene. After making sure that Push appears on the Style menu in the Attributes inspector, enter TestDrive in the inspector’s Identifier field, as I have in Figure 5-16; then press return (or enter).

You won’t always use the identifier, but it's good practice to name it so that you can identify the segue.

image The field in the storyboard isn't updated until you press return, or sometimes until you click in another field in that inspector.

8.     Choose Detail Split from the Attributes inspector’s Destination drop-down menu, as I have in Figure 5-16.

The size of the view in the Test Drive controller changes.

9.     Finally, select the two unused Table View cells in the Document Outline and delete them by pressing Delete. (You won’t be using them.)

The default destination was set to Current, which meant that it was set to the Master view, because that’s where you were dragging from. With that default, the view controller had been resized for the Master view, which is 320 points wide in the standard Split View Controller. But you want the destination to be in the Detail view; choosing Detail Split in this step takes care of that for you.

image

Figure 5-16: Make the destination a Detail view and add an identifier.

If you look closely at Figure 5-16, you can see that the view has now been sized down and that a Navigation bar has been added to the top of the view. If you expand the view controller in the View Controller Scene in the Document Outline, you can see that a Navigation bar was added there as one would expect.

You’ll also notice that a Disclosure Indicator (the chevron — a right-arrowhead-like shape on the right side of the Test Drive cell) has also been added. For now we’ll leave it there, but in Chapter 13, I explain how to remove or change it if you’d like.

When you select the Detail cell and create the Push segue with the Detail view as the destination, the new Test Drive controller becomes embedded in the Detail view’s Navigation controller. This Navigation controller manages the view controller stack for everything in the Detail view of the Split View Controller.

Danger Will Robinson

If you were to build and run RoadTrip for iPad at this point, the app would crash because Apple’s Master-Detail template has added code for dynamic table cells that can’t be used for our static table cell. You will soon delete that unwanted code, so the app will work properly by the end of this chapter.

After the unwanted code is deleted, there will be two ways to test the app for iPad.

First, you could choose Landscape orientation, and select Test Drive, which would allow a new view controller’s view to slide into place, Back button and all. If you then selected the Back button, the old Detail view would slide back into place.

However, in Portrait orientation, if you selected the Master button and then selected Detail, you’d see the new controller with a Back button as well. The Master button, however, will only appear again after you have selected Back and returned to the Detail View controller (the Root View controller). You’ll find out how to fix that annoying bit of business in Chapter 13.

Before you go any further, take a look at the View Controller you just added in the Attributes inspector, which I have selected in the Inspector selector bar in the Utility area in Figure 5-17. As you can see, the controller has properties that you can set using Interface Builder (including a title and identifier which I explain next).

image The Attributes inspector is the place where you'll set properties of the view controllers, controls, and other view objects you add to the view.

image

Figure 5-17: Adding a title and identifier.

Adding an identifier to the view controller

If you take another look at Figure 5-17, you can see that I’ve set Interface Builder’s zoom to full size, clicked on the view controller I just created, selected the Attributes inspector in the Inspector selector bar, and then entered TestDrive in the Title field in the View Controller section. I then selected the Identity Inspector tab, and set the Storyboard ID to TestDriveID. The Title string is used in the Outline view, whereas the Storyboard ID string is used from code in the app.

I did all that for one simple reason: If you want to do anything special with a view controller in a storyboard, you need to be able to find it, and the easiest way to keep tabs on a view controller is by giving it a name using the identifier. Although you may not need an identifier for every view controller, it’s a good idea to get in the habit of adding it just in case you do. As for the name you enter in the Title field, it enables you to distinguish which view controller is which in the storyboard. You’ll notice that in the Document Outline, the selected view controller is now named View Controller – TestDrive and the scene now is named View Controller – TestDrive Scene.

image Whatever you type in a field isn't added until you press Return or click in another field.

View Layout

Before you start to add another view — which will be a subview of the view that's already there — it’s necessary to talk about view layout options. View layout is particularly important on mobile devices because the pesky user might rotate her device. So why is that an issue? Look at the specific case of the original iPad. When held in Portrait orientation, the screen is 768 pixels wide by 1,024 pixels tall. But when rotated to Landscape position, the screen becomes 1,024 pixels wide, but only 768 pixels tall.


Points, pixels, and Retina displays

With the introduction of Retina displays, we have to be careful about how we talk about screen dimensions. In the past, many people used points and pixels interchangeably. Point is a term that was used mostly in typesetting (particularly for print). It is defined as 1/72 of an inch. Pixel is a single element of the screen.

For a long time, most displays supported 72 pixels per inch (PPI). Thus, on those screens, pixels and points were identical. With the Retina display, the pixel count doubled to 144 per inch. This means that every pre-Retina display pixel now was displayed by four pixels (two across and two down). As a result, we can no longer use the two terms interchangeably. The original iPad was 768 x 1,024 pixels and points. An iPad with Retina display is 768 x 1,024 points, but in pixels, it's 1536 x 2,048. As a very rough guide, if you’re using an app such as Photoshop to do your work and you’re worried about resolution, you’re probably worried about pixels. If you’re using Xcode and you want to specify the size and location of objects on the screen, you’re probably using points.


If your app handles rotation properly, components such as buttons, text fields, images, and so on usually need to move and resize. In other words, you need a layout strategy, so that subviews are moved and resized correctly when their containing superview is resized.

Here are three strategies for view layout:

·        Hard-code the layout. Here, you set the location, width, and height of each view yourself, and change those properties when the device is rotated. This is a really bad idea for most apps. It’s difficult, error-prone, and inflexible.

·        Use iOS autosizing. Autosizing has existed since early versions of the iOS SDK and provides a mechanism for automatically moving and resizing a view in response to changes in its superview’s position or size. You can set the default autosizing behavior for your views from the Size pane of the inspector. The Size inspector contains an autosizing section with springs and struts that let you specify which edges of an object (if any) are pinned to the edge of its container (which is often the screen itself) and which edges can move. Although currently supported and used in much older code, it is not the first choice for many developers today.

·        Use Auto Layout. You use the Auto Layout system to define layout constraints for user interface elements. Constraints represent relationships between user interface elements. Auto layout improves upon Autosizing’s “springs and struts” model in many ways. It was introduced in iOS 6 and is the preferred method of handling autosizing today.

Autosizing isn’t just about rotation. Yes, the screen image must adjust as you rotate a device, but when Apple releases a new device with a new screen size, ideally, you want your app to adjust automatically to the new screen size. Using Auto Layout for autosizing can give you a big leg up here.

And there’s even more. If your app will be localized, you need to prepare separate storyboards for each language. Depending on which languages you use, you may have to accommodate scripts that run from right to left as well as from left to right (not to mention languages that can run vertically). On top of that, some languages require more space to express the same thought, although this varies somewhat based on the subject matter of the thought and the language. When you put these localization issues together, you can see that the objects on your storyboard that contain localized text may need to be able to change size to reflect their content. Auto Layout helps to do this. These are advanced issues, but I bring them up here so that if they apply to you and your app, you’ll be aware of Auto Layout and some of its advanced capabilities.

Adding the User Interface Objects

At this point, I could continue building out my storyboard by adding more elements such as views and segues, just like I did in the previous section. But by now I’m itching to really do something, which would require me getting some actual objects into the storyboard. When an itch comes, I scratch, so get ready to start editing and adding some objects.

image To edit an object in the TestDrive view in the storyboard, select Main_iPad.storyboard in the Project editor to open Interface Builder. Expand the View Controller – TestDrive in the View Controller – TestDrive Scene in the Document Outline. Select the View, and then select the Attributes inspector in the Inspector selector.

The Attributes inspector enables you to set various object properties. For example, to change the color of the view’s background, you’d choose the background color from the Background pop-up menu.

If you select Default from the pop-up menu or just click in the current color displayed in the Background field, you see the various default colors (shown on the right side in Figure 5-18). You can also click Other to open the Colors palette.

image

Figure 5-18: Pick a color, any color.

The toolbar at the top of the Colors palette gives you a number of options for selecting a color. These are the standard options available for choosing a color on the Mac, and I’ll leave you to explore them on your own.

Changing the background color for a view is kind of neat, but the real fun comes from your ability to add your own view object as a background image. That’s what you’re going to do next. (Feel the excitement!)

image Any view object you end up using has properties such as color (sometimes), a background image, or the ability to interact with the user — respond to touches, in other words. You generally set these properties using either the Attributes inspector in Interface Builder or programmatically.

To see those properties, you need to zoom to full size or select it in the Document Outline (which zooms it to full size). Full size is the only time view objects are editable.

To add a background image, follow these steps:

1.     Scroll down in the Library window and drag an Image view (the one selected in the Library in Figure 5-19) from the Library onto the view.

An Image view is a view that's used to display an image. (See Chapter 4 for more on views.)

image If you click an object in the Library, up pops a dialog telling you what it is. The dialog even tells you what class it is, as you can see in Figure 5-19. Click Done to dismiss it.

What you just did is add an Image view as a subview of the RoadTrip controller’s view, the one created for you by the template.

image Except for the view controllers up top and a few gesture recognizers scattered around the middle of the gallery, most of the “objects” you see in the Library are derived from the View class.

You can add horizontal and vertical guides to help you line things up by choosing Editor⇒Add Horizontal Guide or Editor⇒Add Vertical Guide, respectively.

image

Figure 5-19: Adding an Image view.

2.     Select the Image view you just added, either in the Document Outline or on the Canvas.

Doing so changes what you see in the Attributes inspector. It now displays the attributes for the Image view.

3.     Using the Attributes inspector’s Image drop-down menu, scroll down to select SeeTheUSA, as shown in Figure 5-20.

Doing so adds the image you want to use to the Image view, as shown in Figure 5-21. (You should have added this image in Chapter — it was in the RoadTrip Resources file you downloaded and added to an asset catalog — either the template's default asset catalog or a separate one for your own images, which I usually call Media.xcassets.)

image The preferred format for the image is .png. Although most common image formats display correctly, Xcode automatically optimizes .png images at build time to make them the fastest and most efficient image type for use in iPad applications.

image

Figure 5-20: Selecting a background image.

image

Figure 5-21: The image selected and in place.

4.     Drag in another Image view and place it at the bottom-center of the Image view you just added, as shown in Figure 5-22.

5.     Return to the Attributes inspector’s Image drop-down menu, but this time select CarImage.

The image you selected is added to the new Image view.

6.     Select the Image view you just added and choose Editor⇒Size to Fit Content from the main menu.

If the command is not enabled, you may need to select it in the Document Outline. The Image view is resized so that it better fits the enclosed content. (See Figure 5-23.) If you look closely, you’ll recognize a vintage pink 1959 Cadillac Eldorado Biarritz convertible.

7.     With the car Image view still selected, choose Editor⇒Add Horizontal Guide and Editor⇒Add Vertical Guide.

I’ll take any help I can get when it comes to positioning objects, and these guides can help me keep track of a particular location on the larger scene.

In Chapter 10, I show you how to animate the Eldorado Biarritz so it leisurely drives to the top of the screen, turns around, drives back, and then turns around one more time so that it's back where it started.

image

Figure 5-22: Adding another image view.

image

Figure 5-23: The family car.

Autosizing with Auto Layout

Because you’re sure to decide — as all good app developers have decided and will continue to decide in perpetuity — that the RoadTrip app needs to function well in both Portrait and Landscape orientations, you’ll need to make sure that, when you rotate the view, the car (subview) remains positioned at the bottom of the screen.

Luckily for you, most of that work gets done for you in UIView­Controller — the class from which we’ve derived TestDriveController. (The technical term for this bit of work is autosizing.) The only thing you have to do is tell the view controller exactly how you want it to move things around when the view changes orientation. You can make such wishes known in the storyboard using the Size inspector.

You’ll need to select some of the views to manage their autosizing. For this process, make certain that the Document Outline is shown at the left of the storyboard. The reason for this becomes clear if you look at the Document Outline in Figure 5-24. Inside the View Controller – TestDrive, you have a view (I’m skipping over the layout guides you added just now). Within that view, you have an image view containing SeeTheUSA image from the asset catalog. The image view is now the same size as its containing view. You need to be able to select each one separately, so the easiest way to do that is in the Document Outline. Figure 5-24 shows the settings you want to set for the Size inspector of the main view.

image

Figure 5-24: Understand the Document Outline view structure.

There are four sections of the Size settings. From top to bottom, they are

·        Metrics: Here at the top of the Size inspector, you set the location coordinates and the view size. Most of the time, I use the Interface Builder tools described in this and the following section, but when I want specific and accurate sizes, I type them in here. (That’s what you’ll do when you add a button soon.)

·        Content Hugging Priority and Content Compression Resistance Priority: These sections are used mostly for adjusting the view’s size based on its contents. As you see in Figure 5-24, each of these has a slider where you can adjust the horizontal and vertical values. This is not an on/off situation: at runtime, the values are weighed and the highest values win out where there's a conflict. You may set constraints (coming up soon) that are contradictory. For example, you may want a view to be positioned in a certain location based on another view (next to it by 10 points, say). In order to do that, the view may need to be resized. In such a case, a higher hugging priority will mean that view will resist resizing at the expense of not fulfilling the request to position it 10 points away from another view. Compression is the other side of the coin. If the compression resistance has a higher value, resizing that might truncate the view's content will be resisted.

·        Intrinsic Size: This is set to default, which is where you’ll leave it for basic layouts. This is the natural size of the view that may be resized or reshaped.

·        Constraints: These are the heart of Auto Layout. You provide constraints as to the view's location. The ones shown in Figure 5-24, which you’ll create shortly, specify the view’s location relative to its container that, for the top-level view, is the screen window. These constraints may be contradictory in some cases, but you can assign a weight to each one to indicate which constraints are more important than others. At runtime, Auto Layout quickly figures out the optimal collection of constraints that does as much as possible of what you want.

image If you don’t see the Constraints section, Auto Layout may be turned off for your storyboard. To turn it on, select the storyboard in the Project navigator and open the File inspector. The Use Auto Layout option is a check box in the Interface Builder Document section.

The simplest way to set the constraints is shown in Figure 5-25. Select the view in the Document Outline, and choose Editor ⇒ Resolve Auto Layout Issues ⇒ Reset to Suggested Constraints in View Controller.

You now should have the three constraints shown in Figure 5-24. All three have the same basic structure. The first one specifies the trailing space from the selected view (the top-most View object) to the image view. The second specifies the leading space, and the third specifies the bottom space.

image

Figure 5-25: Set View constraints.

You may wonder what leading and trailing are in this context. Remember the comment about localization and the directions in which various languages write? In a left-to-right script such as English, Spanish, French, and the like, the leading space is the space between the left edge of the two views, and trailing is the space between the right edges.

For a right-to-left language such as Hebrew or Arabic, the leading space is the space on the right and the trailing space is the space on the left. Spaces for top and bottom are independent of language direction.

If you select the first one, you’ll see that you can click the gear wheel at the right of each constraint to open more details, as shown in Figure 5-26. The default is quite simple: The spacing between right edges of the top-most View and the image view is zero points. If you explore further, you’ll see that the bottom spacing is the default, which is also zero.

You're not yet done with managing Auto Layout. Select the image view and pin it to the bottom of its superview. Use Editor ⇒Pin ⇒Bottom Space to Superview. This means that it will be the right size when the device is rotated.

Repeat the process for the car image. The defaults show you another aspect of constraints. You’ll have the default bottom space to superview again, but you’ll have a new constraint that aligns the center of the car image to the center of the image view for the X (horizontal) coordinate.

image

Figure 5-26: View and edit constraints.

Although the app won’t run until you enter the code in the following section, trust me that when you do enter that code, you’ll be able to rotate the app in the simulator, as shown in Figure 5-27.

image

Figure 5-27: Try rotating the simulator.

This process is typical of using Auto Layout. Draw your layout in the storyboard scene. Some constraints will be created for you automatically, and you can test to see what happens. You may need to tweak the constraints (such as by pinning an object to a superview). You also may choose to clear all the constraints and reset to the defaults. Another tweak may be necessary. But consider that, in this section, you’ve used two menu commands to make the interface handle rotation as well as new screen sizes and shapes.

Adding the Test Drive button

I say the more objects, the merrier. I want to add a nice Test Drive button which, when clicked, will send our 1959 Cadillac Eldorado Biarritz convertible on its merry way. Here’s how it’s done:

1.     Select the Button in the Library and drag it onto the view.

2.     Click the Attributes Inspector button on the Inspector selector bar.

image The button attributes you’re going to set using the Attributes inspector are all Objective-C properties. Note that you can also set these properties programmatically; I show you how to do that in Chapter 8.

As you can see in Figure 5-28, the Type pop-up menu in the Attributes inspector is set to System. The button is shown in its default State configuration — in this case, unselected and unhighlighted. If you were to click the State Config pop-up menu and choose another configuration, such as Highlighted or Selected, you’d be able to change the font, text color, shadow, background, and other attributes in the Attributes inspector for the selected state.

This particular button, being distinctly generic, is remarkably unexciting. To liven things up just a tad, I’m going to have you add a background image I created. (You’ll find that image in the RoadTrip Resources folder that you should have downloaded and added to your asset catalog in Chapter 3.)

3.     In the Attributes inspector, choose Custom from the Type pop-up menu, as shown in Figure 5-29.

4.     Choose Button from the Background drop-down menu, as shown in Figure 5-30.

So why are you using the Background drop-down menu rather than the Image drop-down menu to place an image as your button? When you choose an image from the Background drop-down menu, you're simply doing that — setting the image for the button. You’ll still add the title and so on using the Attributes inspector. If you were to choose from the Image drop-down menu, you would have included the title as part of the image and you wouldn't have been able to add it in the Inspector.

image

Figure 5-28: Adding a button.

image

Figure 5-29: Creating a custom button.

image

Figure 5-30: Choose a custom image for the button.

5.     Using Size inspector (as shown in Figure 5-31), resize the button to 96 points by 37 points using the width and height in the fields provided.

You can also use the resize handles.

6.     To center the button, select it and choose Editor⇒Align⇒Horizontal Center in Container.

Another constraint will automatically be added.

7.     To center it vertically, select it and choose Editor⇒Align⇒Vertical Center in Container.

The vertical constraint will be added.

8.     Give this button a title (buttons don’t have text; they have titles) by going back to the Attributes inspector, entering Test Drive in the Title field, and pressing Return.

You could double-click the button and enter the Title there and press Return as well. In my experience, though, doing so may cause the button to resize itself.

image You could select a different Background image and title for each button state by cycling through the State Config choices and repeating Steps 2 through 5. I’ll leave that for you to explore on your own.

image

Figure 5-31: Using the Size inspector.

image Whenever you enter text, be sure to press Return. Anything you enter won't change the current setting unless you press Return or click in another field.

Massaging the Template Code

If you have been playing around with the code generated for you by the Master-Detail Application template, you’ve probably discovered that not only can you select the + (plus sign) button to create a timestamp displayed both in the Master and Detail views, but you can also select the Edit button and delete the timestamp entry in the Master (and consequently) Detail views.

All this functionality is built into the template courtesy of the UITableViewController. I explain how Table views work in Chapter 20, but because you are using a segue and static cells in the Master view, you won’t need that functionality in the Master view. But not only don’t you need it, it also actually interferes with the functioning of the segue, which you’ll also understand when I explain Table view (and its UITableViewDelegate and UITableViewDataSource protocols) in detail in Chapter 20.

To deal with this issue, I want you to delete the code in Listing 5-1. Here I'm talking about the code in MasterViewController.m starting with the #pragma mark - Table View statement and up to — but not including — the @end statement. This is the code that implements the Table View functionality, and also another method, prepareForSegue, that lets you pass data to the view controller you are transitioning to — unused here, but explained in detail in Chapter 13.

Listing 5-1: Delete This Code from MasterViewController.m

  #pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
  return 1;
}

- (NSInteger)tableView:(UITableView *)tableView 
numberOfRowsInSection:(NSInteger)section
{
  return _objects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView 
dequeueReusableCellWithIdentifier:@"Cell"];

  NSDate *object = [_objects objectAtIndex:indexPath.row];
  cell.textLabel.text = [object description];
    return cell;
}

- (BOOL)tableView:(UITableView *)tableView 
canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [_objects removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
    }
}

/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
        NSDate *object = [_objects objectAtIndex:indexPath.row];
        self.detailViewController.detailItem = object;
    }
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"showDetail"]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        NSDate *object = [_objects objectAtIndex:indexPath.row];
        [[segue destinationViewController] setDetailItem:object];
    }
}

You’ll also need to delete some code in the viewDidLoad method. This code adds the edit and + (plus) buttons to the Navigation bar. Delete the code in Listing 5-2 that I’ve commented out in bold, underline, and italic in viewDidLoad in MasterViewController.m.

Listing 5-2: Delete the Code in viewDidLoad

  - (void)viewDidLoad
{
  [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//self.navigationItem.leftBarButtonItem = 
                          self.editButtonItem;
//UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
    initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
          target:self action:@selector(insertNewObject:)];
//self.navigationItem.rightBarButtonItem = addButton;
self.detailViewController = (RTDetailViewController *)
   [[self.splitViewController.viewControllers lastObject]  
                                       topViewController];
}

Finally, in Listing 5-3, delete the insertNewObject: method in MasterViewController.m (the action method specified in the + button created in viewDidLoad, which was the selector used).

Listing 5-3: Delete insertNewObject:

  - (void)insertNewObject:(id)sender
{
    if (!_objects) {
        _objects = [[NSMutableArray alloc] init];
    }
    [_objects insertObject:[NSDate date] atIndex:0];
    NSIndexPath *indexPath = 
              [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tableView insertRowsAtIndexPaths:
        [NSArray arrayWithObject:indexPath] 
       withRowAnimation:UITableViewRowAnimationAutomatic];
}

image Occasionally you might make a change to your app (usually having to do with a resource) and then, when you run your app, nothing seems to have changed. When you click Run, Xcode does only what it needs to do to the parts of your app that have changed, and if it gets “confused” (yes, there are bugs in Xcode), your change won’t be linked into the app. If you think that has happened, choose Product⇒Clean, and Xcode will recompile all the pieces of your app. And if that doesn't work, go to the Projects tab in the Organizer and delete Derived Data for your project.

Getting Rid of Warnings

Earlier, I warned you the app would not run until you cleaned up the unneeded code. You should have done that now with the code you just deleted, and the app should run.

You may have some warnings. If you do, it's a good idea to clean them up. If they are related to misplaced views on a storyboard, you have a good deal of help waiting for you. Here's what you can do:

1.     Select the warning for the misplaced view in the Issue navigator.

You'll see details of the warning.

2.     You can adjust the values yourself, but look for another yellow warning icon at the right of the warning and click it.

3.     You'll be given a choice of automatic corrections. Choose the one you want to try, as shown in Figure 5-32.

The solutions are presented in less- to more-extreme order from top to bottom.

image

Figure 5-32: Automati-cally fix misplaced views.

Creating the iPhone User Interface

All the code that you write in Chapters 1 through 5 for the iPad will work fine for the iPhone. That will also be true for later chapters in this book. All you need to do is add the following items to your iPhone storyboard file, in a very similar manner to what you just did for the iPad storyboard:

·        Drag a UIViewController into the iPhone storyboard. Change its class name to TestDriveController and its Storyboard ID to TestDrive.

·        Add a UIImageView, using the SeeTheUSA_iPhone.png image.

·        Add a UIImageView, using the CarImage.png image.

·        Add a Test Drive button.

·        Select the Master View Controller scene in the iPhone storyboard.

·        Select the Table view in the Master View Controller.

·        Use the Attributes inspector to change the Master view from Dynamic Prototypes to Static Cells.

·        Select the Label in the first Table View cell, and change its text to Test Drive.

·        Control-drag from the Table View cell to the TestDriveController; choose a Push segue from the pop-up menu.

·        Select the unused Table View cells from the Table view and delete them.

Now the iPhone app should work in a similar manner to the iPad app.