Hour 10. Creating iOS Application Workflows with Storyboards


What You’ll Learn in This Hour:

How to create multiple scenes in the storyboard

The use of segues to transition between scenes

Ways to share data between scenes

How to build applications with navigation and tab bar controllers


In the preceding hour, you learned how to connect an iOS application UI to code. This took place within a single scene, which is good for simple apps, but limiting for serious development. In this hour, we break through the single-scene limit and introduce the ability to create applications with multiple scenes—in other words, multiple view controllers and multiple views.

You learn how to create new scenes and the new view controller classes needed to back them up. You also explore how to visually define your transitions between scenes and trigger them automatically, or programmatically. Because it is easier to understand these concepts by using them, this hour ends with some hands-on practice for multiscene development.

The Power of Storyboards

Over the past few hours, you have learned how the Interface Builder editor can create interfaces and connect them to code. What you haven’t seen, however, is how iOS applications can use storyboarding to develop their entire workflow.

Many iOS apps lend themselves to a single-view approach—which is all you have seen so far in the storyboard examples. It is rare to download an app that doesn’t have configuration screens, help screens, or other displays of information that go beyond the initial view that is loaded at runtime.

To use features like these in your apps, you must create multiple scenes in your storyboard file. Recall that a scene is defined by the presence of a view controller and a view. Imagine how much functionality you could introduce with unlimited scenes (views and view controllers). With the iOS project storyboard, that’s exactly what you can do.

Not only that, but you can literally “draw” the connections between different scenes. Want to display an information screen if the user touches a Help button? Just drag from your button to a new scene. It “just works.” Figure 10.1 shows a multiscene application design with segues.

Image

Figure 10.1. A multiscene application design.

Storyboard Terminology

Before we head into the specifics of multiscene development, you should learn a few terms. Several of these terms you have learned previously but might not have really had to think about the until now include

View controller: A class that manages the user’s interactions with their iDevice interface.

View: The visual layout that a user sees onscreen.

Scene: A unique combination of view controller and view. Imagine you’re building an image-editing application. You may choose to develop scenes for selecting files to edit, another scene for implementing the editor, another for applying filters, and so on.

Segue: A segue is a transition between scenes, often with a visual transition effect applied. There are multiple different types of segues available depending on the type of view controller you’re using.

Modal Views: A modal view is one that is displayed over top of an original view when user interactions are required. A segue to a modal view is segue you’ll make most often.

Relationship: A “segue” of sorts for certain types of view controllers, such as the tab bar controller. Relationships are created between buttons on a master “tab bar” that display independent scenes when touched.

Storyboard: The file that contains the scene, segue, and relationship definitions for your project.

You need to create new class files to support the requirement for multiple view controllers. If you need a quick refresher on adding new files to Xcode, see Hour 5, “Managing Files in Xcode.” Other than that, the only prerequisite is the ability to Control-drag—something you should be very good at after Hour 9, “Connecting a GUI to Code.”


Watch Out!

The storyboard concepts in this hour require a bit of supporting code, so skimming is not recommended.



A Different Perspective

You have just read about the different pieces that you need to know to create a multiscene application, but this doesn’t necessarily help you conceptualize what Apple’s “storyboarding” concept is trying to achieve.

Think of it this way: A storyboard provides an area where you can sketch out, visually, your application’s visual design and workflow. Each scene is a different screen that your user will encounter. Each segue is a transition between scenes. If you’re the type of person who thinks visually, you’ll find that with a little practice, you can go from a paper sketch of an application’s operation and design to a working prototype in the Xcode storyboard very, very quickly.


The Anatomy of a Multiscene Project

To create an application with multiple scenes and segues, you must first know how to add new view controller and view pairings to your project. For each of these, you also need supporting class files where you can code up the logic for your additional scenes. To give you a better idea of how this works, let’s use a typical iOS Single View Application template as a starting point. Feel free to follow along with an empty iOS project if you want—but we build a simple (but complete) example of a multiscene application at the end of the hour.

The Single View Application template has a single view controller and a single view—in other words, a single scene. This doesn’t mean, however, that we’re stuck with that configuration. You can expand a single-view application to support as many scenes as you want; the template just provides a convenient starting point.

Adding Additional Scenes to a Storyboard

To add a new scene to a storyboard, follow these steps:

1. Open the project’s storyboard file (often MainStoryboard.storyboard) in the Interface Builder editor.

2. Make sure the Object Library (Control+Option+Command+3) is visible and type view controller in the Search field to show the view controller objects that are available, as shown in Figure 10.2.

Image

Figure 10.2. Find the view controller objects in the Object Library.

3. Drag the View Controller object into an empty portion of the Editor area.
The view controller adds itself, with a corresponding view, to your storyboard, and just like that, you’ll have a new scene, as shown in Figure 10.3.

Image

Figure 10.3. Adding a new view controller/view creates a new scene.

You can drag the new view around in the storyboard editor to position it somewhere convenient.


By the Way

If you find it difficult to grab and drag the new view around in the editor, use the object bar beneath it. It provides a convenient handle for moving the object around.


Naming Scenes

After adding a new scene to a project, you’ll notice there’s a bit of a problem brewing in the Document Outline (Editor, Show Document Outline). By default, each scene is named based on its view controller class. By default, Single View Application templates include a view controller class called ViewController, so the Document Outline shows the default scene as View Controller Scene. Once we add a new scene, it doesn’t have a view controller class assigned yet, so it also appears as View Controller Scene. Add another, and the scene also appears as View Controller Scene...and so on.

To deal with the ambiguity, you have two options: First, you can add and assign view controller classes to the new scenes; the scenes adopt the name of the class. This is necessary anyway, but sometimes its nicer to have a plain English name for a scene that can be anything we want without it reflecting the underlying code (John’s Awesome Image Editor Scene makes a horrible name for a view controller class).

This brings us to the second approach: applying your own name to the scene. To label a scene using any arbitrary string you want, select its view controller in the Document Outline, and then open the Identity Inspector and expand the Identity section, as shown in Figure 10.4. Use the Label field to enter a name for the scene. Xcode automatically tacks Scene onto the end, so there’s no need to add that.

Image

Figure 10.4. Label the view controller to help differentiate between scenes.

Adding Supporting View Controller Subclasses

After establishing the new scenes in your storyboard, you need to couple them to actual code. In the iOS Single View Application template, the initial view’s view controller is already configured to be an instance of the ViewController class—implemented by editing the ViewController.h and ViewController.m files. You need to create similar files to support any new scenes that are added.


By the Way

If you’re just adding a scene that displays static content (such as a Help or About page), you do not need to add a custom subclass. You can use the default class assigned to the scene, UIViewController, but you won’t be able to add any interactivity.


To add a new subclass of UIViewController to your project, make sure that the Project Navigator is visible (Command+1), and then click the + icon at the bottom-left corner of the window. When prompted, choose the iOS Cocoa Touch template category, click the Objective-C class icon, and then click Next.

You are asked to name your class. Name it something that differentiates it from other view controllers in your project. EditorViewController is better than ViewControllerTwo, for example. Choose a subclass of UIViewController, as shown in Figure 10.5. If you’re creating the controller for use in an iPad project, check the Targeted for iPad check box, and then click Next.

Image

Figure 10.5. Choose the UIViewController subclass.

Finally, you’re prompted for where to save your new class. Use the group pop-up menu at the bottom of the dialog to choose your main project code group, and then click Create. Your new class is added to the project and ready for coding—but it still isn’t connected to the scene you defined.

To associate a scene’s view controller with the UIViewController subclass, shift your attention back to the Interface Builder editor. Within the Document Outline, select the view controller line for the new scene, and then open the Identity Inspector (Option+Command+3). In the Custom Class section, use the drop-down menu to select the name of the class you just created (such as EditorViewController), as shown in Figure 10.6.

Image

Figure 10.6. Associate the view controller with the new class.

After the view controller is assigned to a class, you can develop in the new scene exactly in the same way you developed the initial scene—but the code will go in your new view controller’s class. This takes us most of the way to creating a multiscene application, but the two scenes are still completely independent. If you develop for the new scene, it’s essentially like developing a new application; there is no way for the scenes to exchange data and no way to transition between them.

Sharing Properties and Methods

As you add multiple view controllers (and any other classes) to your project, there’s a good chance they need to display and exchange information. For your classes to “know about each other” programmatically, they need to import one another’s interface files. For example, if MyEditorClass needs to access properties and methods in MyGraphicsClass, MyEditorClass.h includes #import “MyGraphicsClass.h” at its start.

Simple enough, right? Unfortunately, it isn’t always that easy. If both classes need access to one another, and both try to import the interface file from the other class, you’ll most likely end up with an error because the import lines have just created a reference loop. One class references the other, which references the other, which references the other, and so on.

To deal with this situation, you must change your code around a bit and make use of the @class directive. @class enables an interface file to reference another class without creating a loop. Using the hypothetical MyGraphicsClass and MyEditorClass as examples of classes that both need to reference one another, the references could be added like this:

1. In MyEditorClass.h, add #import “MyGraphicsClass.h”. One half of the two references can be implemented with just an #import; nothing special needs to happen.

2. In MyGraphicsClass.h, add @class MyEditorClass; after the existing #import lines.

3. In MyGraphicsClass.m, add the #import “MyEditorClass.h” line after the existing #import lines.

The first #import is performed normally, but to get around the circular reference, the second class’s #import moves to the implementation file, and a @class directive is added to the second class’s interface file. This may seem convoluted, but it works.

After you have created your new scenes, assigned the view controller classes, and added the appropriate references between classes, you’re ready to create segues—the mechanism that enables you to transition from scene to scene.

Creating a Segue

Creating a segue between scenes uses the same Control-drag mechanism that you saw in Hour 9 for making connections between an object and an outlet. For example, consider a two-scene storyboard where you want to add a button to the initial scene that, when clicked, transitions to the Second Scene. To create this segue, you Control-drag from the button to the second scene’s view controller (targeting either the visual representation of the scene itself or the view controller line in the Document Outline), as shown in Figure 10.7.

Image

Figure 10.7. Control-drag from the object to the new scene’s view controller.

When you release your mouse button, a Storyboard Segues box appears, as shown in Figure 10.8. Here you can choose the style of segue that you’re creating—most likely Modal. A total of five potential options may appear:

Image

Figure 10.8. Choose the segue style to create.

Modal: Transition to another scene for the purposes of completing a task. When finished, we dismiss the scene, and it transitions back to the original view. This is the primary segue we will be using.

Push: Create a chain of scenes where the user can move forward or back. This is used with navigation view controllers.

Replace (iPad only): Replace the current scene with another. This is used in some specialized iPad view controllers. This is used with a popular iPad view controller called the “split-view controller.”

Popover (iPad only): Displays the scene in a pop-up “window” over top of the current view.

Custom: Used for programming a custom transition between scenes.

For most projects, you’ll want to choose a modal transition—which is what we use here. The other segues are used in very specific conditions, some of which we cover later this hour.


Did You Know?

You can create a segue that is not attached to any particular UI element by Control-dragging from one scene’s view controller to another. This creates a segue that you can trigger, in your code, from a gesture or other event.


Configuring a Segue

Once the segue is added to your project, you see a line added to the Editor area that visually ties your two scenes together. You can rearrange the individual scenes within the editor to create a layout that maps how the application will flow. This layout is solely for your benefit; it doesn’t change how the application will operate.

Notice, as well, a representation of it in your Document Outline. The scene that initiates a segue shows a new line in the outline: Segue from <origin> to <destination>. Selecting the segue line gives you the opportunity to configure its style, transition type, identifier, and presentation (iPad only), as shown in Figure 10.9.

Image

Figure 10.9. Configure each segue you add.

The identifier is an arbitrary string that you can use to trigger a segue manually or to identify which segue is underway programmatically (if you have multiple segues configured). Even if you do not plan to use multiple segues, it is a good idea to name this something meaningful (toEditor, toGameView, and so on).

The transition type is a visual animation that is played as iOS moves from one scene to another. You have four possible options:

Cover vertical: The new scene slides up over the old scene.

Flip horizontal: The view flips around horizontally, revealing the new scene on the “back.”

Cross dissolve: The old scene fades out while the new scene fades in.

Partial curl: The old scene “curls up” like a piece of paper, revealing the new scene underneath.

On the iPad, you can also set a presentation attribute. This determines how the new modal view is displayed on the screen. The iPad has more screen real estate than an iPhone, so it can do things a little differently. You can choose from four possible presentation styles:

Form sheet: Sizes the scene smaller than the screen (regardless of orientation), showing the original scene behind it. This, for all intents and purposes, is the iPad equivalent of a “window.”

Page sheet: Sizes the scene so that it is presented in the portrait format.

Full screen: Sizes the view so that covers the full screen.

Current context: Uses the same style display as the scene that is displaying it.


Watch Out!: Match Your Styles with Suitable Transitions

Not all styles are compatible with all transitions. A page curl, for example, cannot take place on a form sheet that does not completely fill the screen. Attempting to use an incompatible combination will result in a crash. So, if you have chosen a bad pair, you’ll find out pretty quickly (or you could review the documentation for the transition/style you plan to use).


After setting the identifier, style, transition, and presentation for a segue, you’re ready to use it. Without the developer writing any code, an application that has followed these steps can now present two fully interactive views and transition between them. What it cannot do, however, is interact with them programmatically. In addition, once you transition from one view to another, you cannot transition back. For that, you need some code. Let’s take a look at how you can create and trigger modal segues programmatically, and, perhaps most important, dismiss a modal segue when you have finished using it.

Controlling Modal Segues Manually

Although it is easy to create segues with a single Control-drag, you need to interact with them manually in several situations. If you create a modal segue between view controllers that you want to trigger manually, for example, you need to know how to initiate it in code. When a user is done with the task in another scene, he also needs a mechanism to dismiss the modal scene and transition back to the original scene. Let’s handle these scenarios now.

Starting the Segue

First, to transition to an scene using a segue that you have defined in your storyboard but don’t want to be triggered automatically, you use the UIViewController instance method performSegueWithIdentifier:sender. For example, within your initial view controller, you can initiate a segue with the identifier toMyGame using the following line:

[self performSegueWithIdentifier:@"toMyGame" sender:self];

That’s it! As soon as the line is executed, the segue starts and the transition occurs. The sender parameter should be set to the object that initiated the segue (it doesn’t matter what that object is)—it is made available as a property during the segue if your code needs to determine what object started the process.

Dismissing a Modal Scene

When you execute a modal segue (either automatically or manually), there’s one teensy problem: There is no way back to your original scene. After users have finished interacting with your view, you’ll probably want to provide them with a means of getting back to where they started. At present, there is no facility in modal segues to allow for this, so you must turn to code. The UIViewController method dismissViewControllerAnimated:completion can be used in either the view controller that displayed the modal scene or the modal scene’s view controller to transition back to the original scene:

[self dismissViewControllerAnimated:YES completion:nil];

The completion block is an optional block of code that is executed when the transition has completed. After you have dismissed a scene presented modally, control is returned to the original scene, and users can interact with it as they normally would.

Passing Data Between Scenes

You know how to create and display scenes, but one very critical piece of the puzzle is missing: the ability to share information between the different scenes in an application. Right now, they act as entirely independent applications—which is perfectly fine if that is your intention, but chances are, you want an integrated user experience. Let’s make that happen.

The most straightforward way for any class to exchange information with any other is through properties and methods that it exposes in its interface file. The only trouble with this is that we need to be able to get an instance of one scene’s view controller from another, and, at present, when using a segue we create visually, these are not entirely obvious.

The prepareForSegue:sender Method

One way to get references to the view controllers in a segue is by implementing the prepareForSegue:sender method. This method is automatically called on the initiating view controller when a segue is about to take place away from it. It returns an instance of UIStoryboardSegue and the object that initiated the segue. The UIStoryboard object contains the properties sourceViewController and destinationViewController, representing the view controller starting the segue (the source) and the view controller about to be displayed (the destination).

Listing 10.1 shows a simple implementation of this approach. In this example, the code transitions from an initial view controller (an instance of ViewController) to a new view controller, which is an instance of a hypothetical EditorViewController class.

Listing 10.1. Use prepareForSegue:sender to Grab the View Controllers


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    ViewController *startingViewController;
    EditorViewController *destinationViewController;

    startingViewController=(ViewController *)segue.sourceViewController;
    destinationViewController=
            (EditorViewController *)segue.destinationViewController;

}


First, the method declares two variables to reference the source and destination controllers. Then they are assigned to typecast versions of the source and destination properties returned by the UIStoryboardSegue object.

Once there is a reference to the destination view controller, however, you can set and access properties on it—even changing the presentation and transition styles before it is displayed. If it is assigned to an instance variable/property, it can be accessed anywhere within the source view controller.

What if you want the destination view controller to send information back to the source? In this case, only the source can communicate with the destination because that’s where the prepareForSegue:sender method is implemented. One option is to create a property on the destination controller that stores a reference to the source controller. Another approach is to use built-in properties of UIViewController that make working with modally presented scenes easy easy easy.


It’s Not Just for Getting the Controllers

The prepareForSegue:sender isn’t just for getting the view controllers involved in a segue. It can also be used to make decisions during a segue. Because a scene can define multiple different segues, you may need to know which segue is happening and react accordingly. To do this, use the UIStoryboardSegue property identifier to get the identifier string you set for the segue:

       if ([segue.identifier isEqualToString:@"myAwesomeSegue"]) {
           // Do something unique for this segue
       }


The Easy Way

The prepareForSegue:sender gives us a generic way to work with any segue that is taking place in an application, but it doesn’t always represent the easiest way to get a handle on the view controllers involved. For modal segues, the UIViewController class gives us properties that make it easy to reference the source and destination view controllers: presentingViewController and presentedViewController.

In other words, you can reference the original (source) view controller within a view controller that has just been displayed by accessing self.presentingViewController. Similarly, you can get a reference to the destination view controller from the original controller with self.presentedViewController. It’s as easy as that. For example, assume that the original view controller is an instance of the class ViewController and that the destination view controller is an instance of EditorViewController.

From the EditorViewController, you can access properties in the original view controller with the following syntax:

((ViewController *)self.presentingViewController).<property>

And within the original view controller, you can manipulate properties in the destination view controller with this:

((EditorViewController *)self.presentedViewController).<property>

The parentheses with the class name is necessary to typecast the presentingViewController/presentedViewController properties to the right object types. Without this notation, Xcode would not know what types of view controllers these were, and we wouldn’t be able to access their properties.

Making Advanced Segues

Segues can do more than just link two scenes together: They can work with specialized iOS view controllers to add advanced functionality to applications. In this section, we review how segues work with two common types of iOS controllers: navigation controllers and tab bar controllers.

Storyboarding Navigation Controllers

The navigation controller (UINavigationController) class presents a series of scenes that represent hierarchical information. In other words, one scene presents a high-level view of a topic, a second scene drills down further, a third scene even further, and so on. For example, the iPhone version of the Contacts application presents a list of contact groups. Touching a group opens a list of contacts within that group. Touching an individual contact displays details on that person, as shown in Figure 10.10. At any point in time, a user can back out of a level of detail and return to the previous level—or jump all the way to the starting point, called the root.

Image

Figure 10.10. Navigation controllers are prevalent in iOS.

Managing this transition between scenes is the navigation controller. It creates a “stack” of view controllers. The root view controller is at the bottom. As a user navigates deeper into the scenes, each successive view controller is pushed on the stack, with the current scene’s view controller at the very top. To return to a previous level, the navigation controller pops the topmost controller off the stack and returns to the one below it.


Did You Know?

The terminology of push and pop is used to describe navigation controllers throughout the iOS documentation. You’ll even be showing new scenes under a navigation controller by using the push segue.


Navigation Bars, Items, and Bar Button Items

In addition to managing the stack of view controllers, the navigation controller manages a navigation bar (UINavigationBar). A navigation bar is populated from an instance of a navigation item (UINavigationItem) that is added to each scene that falls under the navigation controller.

By default, the navigation item for a scene contains a title for that scene and a Back button. The Back button is added as a bar button item (UIBarButtonItem) within the navigation item. You can even drag additional bar button items into the navigation item to add your own custom buttons to the navigation bar that is displayed for that scene.

I fully expect that if you have made it through that description, you’re getting a bit worried about having to manually handle all of those different objects (and that’s why doing this in code is not trivial). Don’t fear. Interface Builder makes it painless, and after you see how each scene is constructed, you’ll have no problem working with all these objects in your apps.

Using Navigation Controllers with Storyboards

Adding a navigation controller to a storyboard is very similar to adding a view controller. It looks a bit different, but the process is the same. Let’s assume you’re starting with a Single View Application template. Now, just follow these steps:

1. Establish the code files for one or more view controller subclasses to handle the user’s interactions within a given navigation controller scene. This is the same as any other scene.

2. Open the application storyboard file in the Interface Builder editor.

3. If you want your entire application to fall under the navigation controller, select the view controller in the default view and delete it. (Remove the corresponding ViewController.m and .h files, as well.) This removes the default scene.

4. Drag an instance of the Navigation Controller object from the Object Library into the Document Outline or the Editor area. This adds what appears to be two scenes to your project, as shown in Figure 10.11.

Image

Figure 10.11. Add a navigation controller to your project.

The scene labeled Navigation Controller Scene represents the navigation controller. It is just a placeholder for the object that is going to control all the scenes that fall underneath it. Although you won’t want to change much about the controller, you can use the Attributes Inspector to customize its appearance slightly (choosing a color scene/tint, for example, if you want).

The navigation controller is connected via a relationship to a table view controller scene called Root View Controller. This is the scene where you begin your editing. Although Apple initially gives you a table view controller (UITableViewController) as your Root View Controller scene, you can replace it with anything you want, including a simple view controller (UIViewController). Whatever scene you present will have the navigation bar at the top, and you’ll be able to use a push segue to transition to another scene.

Setting the Navigation Bar Item Attributes

To change the title in the navigation bar, just double-click and start editing, or select the navigation item in the scene and open the Attributes Inspector (Option+Command+4), as shown in Figure 10.12.

Image

Figure 10.12. Customize the Navigation Item for the scene.

You can change three attributes:

Title: The title string that is shown at the top of the view

Prompt: A line of text that provides instruction to the user (if needed) and is shown above the title

Back button: The text that appears in the Back button of the next scene

Yes, you can edit the text of the button that appears in a scene you don’t even have yet. By default, when you transition from one navigation controller scene to another, the “title” of the previous scene shows up as the title of the Back button in the next scene. Sometimes, however, the title may be long, or not necessarily appropriate. In these cases, you can set the Back button attribute to whatever string you want, and if the user drills down to the next scene, that text is displayed in the button that takes you back to the scene.

Editing Back button text does one additional thing: Because iOS can no longer use its default behavior to create a Back button, it creates a new custom bar button item within the navigation item that contains the title you wanted. You can customize this bar button item even more—changing its color and appearance using the Attributes Inspector.

So far, there is only a single scene under the navigation controller, so the Back button would never be displayed. Let’s see how you can chain together multiple scenes to create the drill-down hierarchy that navigation controllers are known for.

Adding Additional Navigation Scenes with Push Segues

To add an additional scene to the navigation hierarchy, we follow the exact same process as adding a new modally presented scene to an application, as follows:

1. Establish the Root View Controller scene to be whatever you want, and include a control that will start the segue. If you want to trigger the segue manually, you don’t need anything extra—you’ll be connecting view controller to view controller.

2. Drag a new view controller instance into the Document Outline or Editor area. This creates a new empty scene with no navigation bar, no navigation item, just an empty scene.

3. Control-drag from the object that you want to trigger the segue to the new scene’s view controller.

4. When prompted for a segue type, choose Push.

You’ll see a new segue line added to the originating scene, as well as bunch of changes to the scene you just connected. The new scene shows the navigation bar and automatically has its own navigation item added and displayed. You can customize the title and Back button, add additional bar button items, the works.

What’s even more important to realize is that you can keep doing this. You can add additional push segues—even branch from multiple segues to follow different paths, as shown in Figure 10.13. Xcode keeps track of everything for you.

Image

Figure 10.13. Create as many push segues as you need (even branches).


Sharing Data Between Navigation Controller Scenes

Wondering how to share data between all the different scenes in a navigation controller-based application? The navigation controller instance itself provides a perfect place to share data. By creating and using a subclass of the UINavigationController, we can access this class using the parentViewController attribute from any of the scenes we create.


Understanding Tab Bar Controllers

The second type of view controller covered this hour is the tab bar controller (UITabBarController). Tab bar controllers, like navigation controllers, are prominently featured in a wide range of iOS applications. As the name implies, a tab bar controller presents a series of tabs at the bottom of the screen—represented as icons and text—that can be touched to switch between scenes. Each scene represents a different function in the application, or a unique way of viewing the application’s information.

The Phone application on the iPhone, for example, presents different ways of sorting your calls using a tab bar controller, as shown in Figure 10.14.

Image

Figure 10.14. A tab bar controller switches between unique scenes.

Tab Bars and Tab Bar Items

Like a navigation controller, the tab bar controller handles everything for you. When you touch a button to transition between scenes, it just works. You do not have to worry about programmatically handling tab bar events or manually switching between view controllers. The similarity doesn’t end there.

A tab bar controller also contains a UITabBar—a UI element that resembles a toolbar, but in appearance only. Any scene that is presented with the tab bar controller inherits this navigation bar within its scene.

The scenes presented by a tab bar controller must contain a tab bar item (UITabBarItem) that has a title, an image, and if desired, a badge (a little red circle with a number in it).


Watch Out!: The Unused Tabbed Template

Before you start building tab-based applications, I want to point out that Apple includes an iOS application template called the Tabbed Application. This template creates an application with two sample tabs already added, and a two view controller subclasses set up and associated with each tab. It also makes absolutely no sense (to me) to use.

This template may get you up and running a few seconds faster than adding a tab bar controller to a storyboard, but for production projects, it has a fatal flaw: Apple has associated two view controllers with the two default tabs in the application and named them FirstViewController and SecondViewController. There’s nothing wrong with this for learning exercises, but in a real application, you want to name these in a way that reflects their actual use (MovieListViewController, TheaterListViewController, and so on). You could certainly rename all of their references in Xcode, but by the time you did that, it would have been faster to just add and associate your own tab bar controller and view controller subclasses.


Using Tab Bar Controllers with Storyboards

To add a tab bar controller to an application, start with the Single View Application template. If you do not want the initial scene to segue into the tab bar controller, just delete the initial scene by removing its view controller, and then delete the corresponding ViewController interface and implementation files. When your storyboard is in the state you want, drag an instance of the Tab Bar Controller object from the Object Library into the Document Outline or the Editor area. This adds a controller and two sample tab bar scenes to the view, as shown in Figure 10.15.

Image

Figure 10.15. Adding a tab bar controller adds two default scenes to the application.

The Tab Bar Controller scene represents the UITabBarController object that coordinates all the scene transitions. Within it is a Tab Bar object that you can customize slightly with Interface Builder, changing the color.

From the tab bar controller are two “relationship” connections to the two scenes that the tab bar will display. The scenes can be differentiated by the name of the tab bar button that is added to them: Item 1 and Item 2, by default.


Did You Know?

Even though all the tab bar item buttons are shown in the Tab Bar Controller scene, they are actually part of the each individual scene. To change the tab bar buttons, you must edit the tab bar item added to a scene. The controller scene is left alone.


Setting the Tab Bar Item Attributes

To edit the tab bar item (UITabBarItem) that is displayed for any scene, open that scene’s view controller and select the tab bar item within the Document Outline area, and then open the Attributes Inspector (Option+Command+4), as shown in Figure 10.16.

Image

Figure 10.16. Customize each scene’s tab bar item.

Using the Tab Bar Item settings section, you can set a value to be displayed in the tab bar item badge. Typically, you want to set this via tab bar item’s badgeValue property (an NSString) in code. You can also use the Identifier pop-up menu to choose from over a dozen predefined tab bar icons and labels. If you choose to use a predefined icon/label, you cannot customize it further because Apple wants these to remain constant throughout iOS.

To set your own image and title, use the Bar Item settings section. The Title field sets the label for the tab bar item, and the Image drop-down associates an image resource from your project for the item.

That’s everything you need to configure a scene for a tab bar controller. But what if you want to add additional scenes to the tab bar? We tackle that now, and as you’ll see, it’s even easier than adding a scene to a navigation controller.

Adding Additional Tab Bar Scenes

Unlike other segues that we’ve looked at, a tab bar has a clearly defined item (the tab bar item) that triggers a change in scene. The scene transition isn’t even called a segue—it is a “relationship” between the tab bar controller and a scene.

To create a new scene, tab bar item, and the relationship between the controller and scene, start by adding a new view controller to the storyboard, as follows:

1. Drag a new view controller instance into the Document Outline or Editor area.

2. Control-drag from the Tab Bar Controller object to the new scene’s view controller in the Document Outline.

3. When prompted, choose Relationship - viewControllers, as shown in Figure 10.17.

Image

Figure 10.17. Create a relationship between controllers.

Creating the relationship does everything we need—it automatically adds a tab bar item to the new scene, ready to be configured. We can keep doing this to create as many tabs and scenes as we need in the tab bar.


Sharing Data Between Tab Bar Scenes

Like the navigation controller, a tab bar controller presents us with an easy opportunity to share information. Create a tab bar controller (UITabBarController) subclass that is assigned as the identity of the tab bar controller. Add properties to the subclass that represent the data we want to share, then access those properties through the parentViewController property in each scene.


A Navigation Storyboard Example

To conclude this hour, we create an application that gives us a chance to practice the skills discussed in this hour, and several of those introduced in the preceding two hours. We build an application, LetsNavigate, that presents a series of three scenes through a navigation controller (see Figure 10.18). Within each scene, we show a Push button that increments a counter and then transitions to the next scene. The counter is stored in a custom subclass of the navigation controller. In other words, this provides both an example of building a navigation-based UI and of using the navigation controller to manage a property that all the scenes can access.

Image

Figure 10.18. Building multiscene navigation controller example.

Implementation Overview

We start with a Single View application template, remove the initial scene and view controller, and then add a navigation controller and two custom classes—one a subclass of a navigation controller that will enable each scene in the application to share information, the other a subclass of a view controller that will handle user interactions in the scenes.

We will remove the default table view root scene added with the navigation controller and add three additional scenes. A Push button is included in each scene’s view with an action method to increment a counter—as well as a segue from that button to the next scene.

Setting Up the Project

Create a new Single View iPhone project called LetsNavigate. Before doing anything else, clean up the project so we only have the things that we need. Start by selecting the ViewController class files (ViewController.h and ViewController.m) and pressing the Delete key. When prompted, choose to move the files to the trash, not just the references.

Next, click the MainStoryboard.storyboard file and then select the View Controller line in the Document Outline area (Editor, Show Document Outline) and again press Delete. The scene disappears. We now have the perfect starting point for our app.

Adding the Navigation Controller and Generic View Controller Classes

We need two additional classes added to the project. The first, a subclass of UINavigationController manages our push count property and is named CountingNavigationController. The second, a subclass of UIViewController, is named GenericViewController and handles incrementing the push count as well as displaying the count in each scene. To add these classes, follow these steps:

1. Click the + button at the bottom-left corner of the Project Navigator.

2. Choose the iOS Cocoa Touch category and the Objective-C class, and then click Next.

3. Name the new subclass CountingNavigationController (you will have to type the class name in), set it to be a subclass of UINavigationController, and click Next.

4. On the last setup screen, choose your main project code group from the Group pop-up menu, and then click Create.

5. Repeat this process to create a new UIViewController subclass named GenericViewController. Make sure you choose the right subclass for each of the new classes; otherwise, you’ll have difficulty later on.

Adding the Navigation Controller

To add the navigation controller, follow these steps:

1. Open the MainStoryboard.storyboard in the Interface Builder editor.

2. Display the Object Library (Control+Option+Command+3) and drag a Navigation Controller object into an empty area of the Interface Builder editor (or into the Document Outline area).

Your project will now show a Navigation Controller Scene and a Root View Controller Scene.

3. The Root View Controller scene is, by default, a table view controller-based scene. We don’t want this, so select the table view controller in the Document Outline and press Delete. The scene disappears.

4. Now, concentrate on the Navigation Controller scene. We want to associate this controller with our CountingNavigationController class, so select the Navigation Controller line in the Document Outline and open the Identity Inspector (Option+Command+3).

5. From the class drop-down menu, choose CountingNavigationController.

Done.

Now let’s add the three additional scenes we need and associate them with the generic view controller class we created.

Adding Additional Scenes and Associating the View Controller

With the storyboard still open, drag three instances of the View Controller object from the Object Library into the Editor area or the Document Outline. In a few minutes, these will be connected to the Navigation Controller scene to form a series of scenes to create a managed application workflow.

After adding the additional scenes, you want to do two things to each of them. First, set the identity of each scene’s view controller. In this case, one view controller class is handling all of them, so the identity is set to GenericViewController. Next, it’s a good idea to set a label for each view controller so that the scene has a friendlier name. To do so, follow these steps:

1. Start by selecting the first (whichever you decide is “first” is fine) scene’s View Controller object and opening the Identity Inspector (Option+Command+3).

2. Use the Class drop-down menu to pick the GenericViewController.

3. Still within the Identity Inspector, set the Label field to First.

4. Move to one of the other scenes you added, select its view controller line, set its class to GenericViewController, and the label to Second.

5. Repeat the process for the last scene as well—setting its custom class and a label of Third.

When finished, your Document Outline should look like Figure 10.19.

Image

Figure 10.19. Your final Document Outline includes a navigation controller and three scenes—order is not important.

Planning the Variables and Connections

I’m intentionally trying to keep these projects light so that there isn’t a great deal of information that needs to be stored or actions that have to be defined. The CountingNavigationController will have a single property, pushCount, that contains the number of times we have pushed a new scene into view using the navigation controller.

The GenericViewController class will have a single property called countLabel that references a label in the UI displaying the current count of pushes. It will also have an action method named incrementCount that will increase the pushCount property in the CountingNavigationController by one.

Creating the Push Segues

To build a segue for the navigation controller, we need something to trigger it. Within the storyboard editor, add a button (UIButton) labeled Push to the First Scene and Second Scene, but not the Third Scene. Why not the Third? Because it is the last scene that can be displayed, there’s nothing after it to segue to.

Now, Control-drag from the navigation controller (either in the Document Outline or in the Editor area) to the First Scene. When prompted for a segue, choose Relationship – Root View Controller. This sets the First Scene as the scene that is initially displayed by the navigation controller.

Next, Control-drag from the button in the First Scene to the Second Scene’s view controller line in the Document Outline, or target the scene directly in the editor. When prompted for the segue type, choose Push, as shown in Figure 10.20. A new segue line, Segue from UIButton to Second, is added to the First Scene in the Document Outline, and the Second Scene inherits the navigation controller’s navigation bar and gains a navigation item in its view.

Image

Figure 10.20. Create a push segue.

Repeat this process, creating a push segue from the Second Scene’s button to the Third Scene. Your Interface Builder editor should now contain a fully realized navigation controller sequence. Click and drag each scene in the view to arrange it in a way the makes sense to you. Figure 10.21 shows my interconnected views.

Image

Figure 10.21. Connect all of your views via segues.

Creating the Interface

By adding the scenes and buttons, you have really just built most of the interface. The final steps are customizing the title of the navigation item in each scene and adding an output label to display the push count.

Begin by going through each of the scenes—First, Second, And Third—and double-clicking the center of the navigation bar that now appears at the top of each view. Title the first view First Scene, the second Second Scene, and the third... wait for it... Third Scene.

Finally, to each of the scenes add a label (UILabel) near the top that reads Push Count: and a second label (the output label) with the default text of 0 (and a large, center aligned font, if you want) to the center of each view.

Figure 10.22 shows the final interface design.

Image

Figure 10.22. The final layout of the navigation application.

Creating and Connecting the Outlets and Actions

There is only one outlet and one action that need to be defined in this project—but they need to be connected several times. The outlet—a connection to the label displaying the push count (countLabel)—will be connected to each of the three scenes. The action, incrementCount, will only need to be connected to the button in the First Scene and Second Scene.

Position your display in the Interface Builder editor so that the First Scene is visible (or just use the Document Outline), click its push count label, and then switch to the Assistant Editor mode.

Adding the Outlet

Control-drag from the label in the center of the First Scene to the just below the @interface line in GenericViewController.h. When prompted, create a new outlet named countLabel.

That created the outlet and the connection from the First Scene; now you need to connect it to the other two scenes. Control-drag from the Second Scene’s push count label and target the countLabel property you just created. The entire line will highlight, as shown in Figure 10.23, showing you are making a connection to an existing outlet. Repeat this for the Third Scene, connecting its push count label to the same property.

Image

Figure 10.23. Create the outlet, and then connect the other scenes’ labels.

Adding the Action

Adding and connecting the action works in much the same way. Start by Control-dragging from the First Scene’s button to just below the property definition in GenericViewController.h. When prompted, create a new action named incrementCount.

Switch to the second view controller and Control-drag from its button to the existing incrementCount action. You’ve just made all the connections we need.

Implementing the Application Logic

Most of our work is now behind us. To finish the tutorial, we first need to set up the pushCount property in the CountingNavigationController class so that it can keep track of the number of times we have pushed a new scene in the application.

Adding the Push Count Property

Open the CountingNavigationController.h interface file and add a property definition for an integer named pushCount below the @interface line:

@property (nonatomic) int pushCount;

Next, open the CountingNavigationController.m file and add a corresponding @synthesize statement below the existing @implementation line:

@synthesize pushCount;

That’s all we need to do to implement the custom CountingNavigationController class. Because it is a subclass of a UINavigationController, it already performs all the navigation controller tasks we need, and now it stores a pushCount property, too.

To access this property from the GenericViewController class that is handling the content for all the scenes in the application, we need to import the custom navigation controller’s interface file in GenericViewController.h. Add this line following the existing #import statement:

#import "CountingNavigationController.h"

We’re all set to finish our implementation, which is just a matter of adding the logic to GenericViewController that increments the counter and makes sure it is displayed on the screen when a new scene is pushed into view.

Incrementing and Displaying the Counter

To increment the counter in GenericViewController.m, we use the parentViewController property to access the pushCount property. The parentViewController, as you have learned, is automatically set to the Navigation Controller object within any scene managed by the navigation controller.

We need to typecast the parentViewController to our custom class of CountingNavigationController, but the full implementation is just a single line. Implement incrementCount as shown in Listing 10.2.

Listing 10.2. The incrementCount Implementation


- (IBAction)incrementCount:(id)sender {
    ((CountingNavigationController *)self.parentViewController).pushCount++;
}


The final step is to update the display to show the current count. Because pushing the button increments the push count and pushes a new scene into view, the incrementCount action is not necessarily the best place for this logic to fall. In fact, it won’t always be accurate because the count could be updated in another view and then the Back button used to “pop” back to the original view, which would now be showing an invalid count.

To get around this, we just add the display logic to the viewWillAppear:animated method. This method is called right before a view is displayed onscreen (regardless of whether it is through a segue or by a user touching the Back button), so it is a perfect place to update the label. Add the code in Listing 10.3 to the GenericViewController.m file.

Listing 10.3. Update the Display in viewWillAppear:animated


1: -(void)viewWillAppear:(BOOL)animated {
2:    NSString *pushText;
3:    pushText=[[NSString alloc] initWithFormat:@”%d”,
4:              ((CountingNavigationController *)
5:               self.parentViewController).pushCount];
6:    self.countLabel.text=pushText;
7: }


Line 2 declares a new string, pushText, that will contain a string representation of the counter. Line 3 allocates and initializes this string using the NSString initWithFormat method. The %d format string is replaced by the contents of the pushCount property, accessed using the same approach as in the incrementCount method.

In the last step, line 6, the countLabel is updated with the pushText string.

Building the Application

Run the application and test the navigation controller. Use the button to push new scenes on to the navigation controller stack, and then pop them back off with the Back button functionality that we get for free. The push count stays in sync through all the different scenes because we now have a central class (CountingViewController) managing our shared property for us.

Summary

The topics this hour introduced—multiple scenes and segues—are very important aspects of iOS development that can take your apps from being simple single-view “utility”-style programs to full-featured software. You learned how to visually and programmatically create modal segues and handle interactions between scenes.

In addition, the hour explored two new view controller classes. The first, the navigation controller, displays a sequence of scenes that are displayed one after the other—and are often used to “drill down” into detailed information about something. The second, the tab bar controller, is used to create applications with a single unifying bar at the bottom that can be used to switch between different scenes. The integration of these controllers with storyboard scenes and segues is a elegant and powerful feature of Xcode and iOS.

Q&A

Q. Why doesn’t iOS just provide windows?

A. Can you imagine managing windows with just your fingers? The iOS interface is designed to be touched. It is not meant to model a typical desktop application environment, which was built around the mouse.

Q. Can I mix and match scenes and segues?

A. Yes and no. You cannot use a push segue without a navigation controller, nor create a working tab bar application without a tab bar controller. You can, however, implement a navigation controller that is used in conjunction with a tab bar controller, or display a modal segue that transitions to a navigation controller-managed series of scenes and so on.

Q. What if I want to share information between scenes that do not have a central controller class?

A. The fact that the tab bar controller and navigation controller have a nice place to implement shared properties is great, but not necessary. You can always create a custom singleton class in your application and reference it in other classes that need to exchange data.

Workshop

Quiz

1. Navigation controllers and tab bar controllers require extensive coding with the Storyboard feature. True or false?

2. All presentation and transition styles are compatible with one another. True or false?

3. There is no easy way to share data between scenes in a tab bar or navigation-based application. True or false?

Answers

1. False. The Xcode storyboard makes it possible to add these features almost entirely with drag-and-drop simplicity.

2. False. Some transitions will not work with some presentation styles. You can find the full guidelines in the developer documentation.

3. False. These controllers have a central controller class that offers a great place to share information between scene’s view controllers.

Activities

1. Using the iOS Single View Application template, practice the storyboarding techniques described in this hour—from simple modal segues to navigation controllers and tab bar controllers.

2. Use the iOS Single View Application template (for the iPad) to test the different modal presentation styles available on Apple’s tablet platform.

..................Content has been hidden....................

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