The standard view controller (iOS 4)

The plain view controller is simple to embed inside your program. But why would you want to use a view controller? That’s going to be one of the topics we’ll cover here. Now, we’ll look at how view controllers fit into the view hierarchy, how you create them, how you expand them, and how you make active use of them. Let’s get started with the most basic anatomical look at the view controller.

The anatomy of a view controller

A view controller is a UIViewController object that sits immediately above a view (of any sort). It, in turn, sits below some other object as part of the tree that ultimately goes back to an application’s main window. This is shown in figure 5.1.

When we move on to advanced view controllers in topic 7, you’ll see that the use of a bare view controller can grow more complex. Bare view controllers often sit beneath advanced view controllers, to take care of the individual pages that advanced view controllers let you navigate among.

A bare view controller shows view controlling at its simplest: it sits below one object and above another.


Figure 5.1 A bare view controller shows view controlling at its simplest: it sits below one object and above another.

Looking at the iOS’s class hierarchy, you can see that the UIViewController is a direct descendent of UIResponder, which is a descendent of NSObject. It’s also the parent object of all the other view controllers we’ll discuss. Practically, this means that the lessons learned here also apply to all the other controllers.

But learning about how a view controller works leaves out one vital component: how do you create it?

Creating a view controller

The easiest way to incorporate a plain view controller into your project is to select a different template when you create it. The View-Based Application template should probably be your default template for programming from here on out, because it comes with a view controller built in.

As usual, the template’s work is primarily done visually. When you create a new project (which we’ve called viewex for the purpose of this example), you can verify this by looking up the view controller’s IBOutlet command in the program’s app delegate header file:

tmp12111_thumb

The app delegate’s source code file further shows that the view controller’s view has already been hooked up to the main window:

tmp12112_thumb

This view is a standard UIView that’s created as part of the template. Although a view controller has only one view, that view may have a variety of subviews, spreading out into a hierarchy. We’ll show you how to add a single object beneath the view in a moment, and you’ll make more complete use of it in the next topic. But before we get there, we want to step back and look at how you can create a view controller by hand if you need to.

Creating another view controller

Creating another view controller is simple. First, drag a view controller from the Library to your xib document window. Alternatively, you can alloc and init an object from the UIViewController class.

Second, note that the previous IBOutlet command shows that the controller isn’t instantiated directly from the UIViewController class. Rather, it’s instantiated from its own subclass, which has its own set of files (viewexViewController.{h|m}), named after the example project’s name. This is standard operating procedure.

Because you want a view controller to do event management, you’ll often need to modify some of the controller’s standard event methods, so you require your own subclass. To start, the view controller class files are mostly blank, but Xcode helpfully highlights a number of standard view controller methods that you may want to modify.

After you’ve finished creating a bare view controller, you’re mostly ready to go. But you have a slight opportunity to modify the view controller for your specific program, and that’s what we’ll cover next.

Building up a view controller interface

In order to correctly use a view controller, you need to build your view objects as sub-views of the view controller, rather than subviews of your main window or whatever else lies above it. This is easy to do both programmatically and visually.

THE PROGRAMMATIC SOLUTION

The view controller class file gives you access to a pair of methods that can be used to set up your view controller’s views. If the view controller’s view is linked to a .xib file, you should use viewDidLoad, which will do additional work after the .xib is done loading; if you didn’t first create it visually, you should instead use loadView.

Before you do any of this, your view controller will always start off with a standard UIView as its one subview. But by using these methods, you can instead create the view controller’s view as you see fit, even creating a whole hierarchy of subviews if you desire.

The following code adds a simple UILabel to your view controller using viewDid-Load. In the following listing, we’ve chosen a humongous font that is automatically sized down so that later we can show off how rotation and resizing work:

Listing 5.1 Add a UILabel to your view controller

Listing 5.1 Add a UILabel to your view controller

The self.view line is the only one of particular note O. It connects your label object as a subview of the view controller’s UIView.

This example is also noteworthy because it’s the first time you’ve definitively moved outside of your app delegate for object creation. You could have done this object creation in the app delegate, but that’s often sloppy programming because this needs to be done in the view controller. Now that you have view controllers, you’ll increasingly do your work in those class files. This not only better abstracts your object creation but also kicks off your support of the MVC model, because you now have controllers instantiating the views they manage. Watch for a lot more of this in the future. We’ll also briefly return to the viewDidLoad and loadView methods when we talk about the bigger picture of the view controller lifecycle, shortly.

THE VISUAL SOLUTION

In the last topic, we noted that view controllers often have their own .xib files, allowing you to have one .xib file for each page of content. That’s what’s going on in the program you created from the View-Based Application template. At creation, the template contains two .xib files: MainWindow.xib and viewexViewController.xib.

The MainWindow.xib file contains a view controller and a window. The all-important link to the second .xib file can be found here. If you click the view controller’s Attribute tab, it helpfully shows you that the controller’s content is drawn from viewexView-Controller(.xib). This is shown in figure 5.2.

To hook up a new .xib file to a view controller, enter its name in the view controller's attributes under NIB Name.

Figure 5.2 To hook up a new .xib file to a view controller, enter its name in the view controller’s attributes under NIB Name.

Now that you understand the hierarchy of .xib files that’s been set up, how do you make use of them? In order to create an object as a subview of the view controller, you need to place it inside the .xib file that the view controller manages—in this case, viewexViewController.xib. To add a UILabel to your view controller, you call up the viewexViewController.xib file and then drag a label to the main display window, which should represent the existing view. Afterward, you can muck with the label’s specifics in the inspector window, as usual.

Practically, there’s nothing more you need to do to set up your basic view controller, but we still need to consider a few runtime fundamentals.

Using your view controller

If you’ve chosen to use a standard view controller, it should be because you’re only managing one page of content, not a hierarchy of pages. In this situation, you don’t need your view controller to do a lot, but your view controller is still important for three things, all related to event management:

■ It should act as the hub for controlling its view and subviews, following the MVC model. To do this, it needs easy access to object names from its hierarchy.

■ It should control the rotation of its view, which will also require resizing the view in rational ways. Similarly, it should report back on the device’s orientation if queried.

■ It should deal with lifecycle events related to its view.

We’ve split these main requirements into six topics, which we’ll cover in turn.

PUTTING THE MVC MODEL TO USE

Although we’ve talked about the Model-View-Controller (MVC) architectural pattern, you haven’t yet put it to real use. Up to this point, it’s been a sort of abstract methodology for writing programs. But now that you’re ready to use view controllers, you can start using MVC as a real-world ideal for programming.

As you’ll recall, under MVC, the model is your backend data and the view is your fron-tend user interface. The controller sits in between, accepting user input and modifying both of the other entities. The view controller should take the role of the controller in the MVC, as the name suggests. We’ll get into this more in the next topic, but we can say confidently that event and action control will happen through the view controller.

We can say this confidently because you’ll pretty much be forced into using MVC. A view controller is automatically set up to access and modify various elements of views that sit under it. For example, the view controller has a title property that is intended to be a human-readable name for the page it runs. In topic 7, you’ll learn that tab bars and navigation bars automatically pick up that information for their own use. In addition, you’ll often see view controllers automatically linked up to delegate and datasource properties, so that they can respond to the appropriate protocols for their subviews.

When you start seeing view controllers telling other objects what to do, look at it through the MVC lens. You should also think about MVC as you begin to program more complex projects using view controllers.

FINDING RELATED ITEMS

If a view controller is going to act as a controller, it needs easy access to the objects that lie both above and below it in the view hierarchy. For this purpose, the view controller contains a number of properties that can be used to find other items that are connected to it. They’re listed in table 5.2.

These properties will be useful primarily when we move on to advanced view controllers, because they’re more likely to link multiple view controllers together. We’re mentioning them here because they’re related to the idea of MVC and because they’re UIViewController properties that will be inherited by all other types of controllers.

For now, we’ll leave these MVC-related properties and get into some of the more practical things you can immediately do with a view controller, starting with managing view rotation.

Table 5.2 When you begin connecting a view controller to other things, you can use its properties to quickly access references to those other objects.

Property

Summary

modalViewController

Reference to a temporary view controller, such as the Address Book and photo roll controllers that we’ll discuss in chapter 8 and 11.

navigationController

Reference to a parent of the navigation controller type.

parentViewController

Reference to the immediate parent view controller, or nil if there is no view controller nesting.

tabBarController

Reference to a parent of the tab bar controller type.

tabBarItem

Reference to a tab bar item related to this particular view.

view

Reference to the controller’s managed view. The view’s subviews property may be used to dig further down in the hierarchy.

ROTATING VIEWS

Telling your views to rotate is simple. In your view controller class file, you’ll find a method called shouldAutorotateToInterfaceOrientation:. In order to make your application correctly rotate, all you need to do is set that function to return the Boolean YES, as shown here:

tmp12115_thumb

At this point, if you compile your program, you’ll find that when you rotate your iPhone or iPad, the label shifts accordingly. Even better, because you set its font size to vary based on the amount of space it has, it gets larger when placed horizontally. This is a simple application of modifying your content based on the device’s orientation.

You should consider one additional thing when rotating your views: whether they will resize to account for the different dimensions of the new screen.

RESIZING VIEWS

When you change your device’s orientation from portrait to landscape, you change the amount of space for displaying content—for example, an iPhone goes from 320 x 480 to 480 x 320. As you just saw, when you rotated your label, it automatically resized, but this doesn’t happen without some work.

A UIView (not the controller!) contains two properties that affect how resizing occurs. The autoresizesSubviews property is a Boolean that determines whether autoresizing occurs. By default, it’s set to YES, which is why things worked correctly in the first view controller example. If you instead set it to NO, your view will stay the same size when a rotation occurs. In this case, your label will stay 320 pixels wide despite now being on a 480-pixel wide screen.

After you’ve set autoresizesSubviews, which says that resizing will occur, your view looks at its autoresizingMask property to decide how it should work. The autoresizingMask property is a bitmask that you can set with the different constants listed in table 5.3.

Table 5.3 autoresizingMask properties allow you to control how your views resize.

Constant

Summary

UIViewAutoresizingNone

No resizing

UIViewAutoresizingFlexibleHeight

Height resizing allowed

UIViewAutoresizingFlexibleWidth

Width resizing allowed

UIViewAutoresizingFlexibleLeftMargin

Width resizing allowed to left

UIViewAutoresizingFlexibleRightMargin

Width resizing allowed to right

UIViewAutoresizingFlexibleBottomMargin

Height resizing allowed to bottom

UIViewAutoresizingFlexibleTopMargin

Height resizing allowed to top

If you want to modify how your label resizes programmatically, you can do so by adding the following lines to viewDidLoad:

tmp12116_thumb

Note again that these resizing properties apply to a view, not to the view controller. You can apply them to any view you’ve seen so far. There has been little need for them before you started rotating things.

Modifying the way resizing works is even easier if you do it visually. If you recall, the Resize tab of the inspector window contains an Autosizing section, as shown in figure 5.3.

Here you can see exactly what autoresizing looks like.

Figure 5.3 Here you can see exactly what autoresizing looks like.

You can click six different arrows that correspond to the six resizing constants other than None. Highlighting an individual arrow turns on that type of resizing. The graphic to the right of these arrows serves as a nice guide to how resizing will work.

CHECKING ORIENTATION

Now that you have an application that can rotate at will, you may occasionally want to know what orientation a user’s iPhone or iPad is sitting in. You do this by querying the interfaceOrientation view controller property. It’s set to one of four constants, as shown in table 5.4.

You don’t have to have a view controller to look up this information. A view controller’s data is kept in tune with orientation values found in the UIDevice object—a useful object that also contains other device information, such as your system version. We’ll talk about it in topic 10.

Table 5.4 The view controller’s interfaceOrientation property tells you the current orientation of an iPhone or iPad.

Constant

Summary

UIInterfaceOrientationPortrait

Device is vertical, right side up.

UIInterfaceOrientationPortraitUpsideDown

Device is vertical, upside down.

UIInterfaceOrientationLandscapeLeft

Device is horizontal, tilted left.

UIInterfaceOrientationLandscapeRight

Device is horizontal, tilted right.

MONITORING THE LIFECYCLE

We’ve covered the major topics of loading, rotating, and resizing views within a view controller. With that under your belt, we can now look at the lifecycle events that may relate to these topics.

You saw lifecycle events in topic 2, where we examined methods that alert you to the creation and destruction of the application, and some individual views. Given that one of the purposes of a controller is to manage events, it shouldn’t be a surprise that the UIViewController has several lifecycle methods of its own, as shown in table 5.5.

Table 5.5 You can use the view controller’s event-handler methods to monitor and manipulate the creation and destruction of its views.

Method

Summary

loadView:

Creates the view controller’s view if it isn’t loaded from a .xib file.

viewDidLoad:

Alerts you that a view has finished loading. This is the place to put extra startup code if loading from a .xib file.

viewWillAppear:

Runs just before the view loads.

viewWillDisappear:

Runs just before a view disappears — because it’s dismissed or covered.

willRotateToInterfaceOrientation:duration:

Runs when rotation begins.

didRotateToInterfaceOrientation:

Runs when rotation ends.

You’ve met loadView and viewDidLoad, which are run as part of the view controller’s setup routine and which you used to add extra subviews. The viewWillAppear: message is sent afterward. The rest of the messages are sent at the appropriate times, as views disappear and rotation occurs.

Any of these methods can be overwritten to provide the specific functionality that you want when each message is sent.

OTHER VIEW METHODS AND PROPERTIES

The view controller object contains a number of additional methods that can be used to control exactly how rotation works, including controlling its animation and what header and footer bars slide in and out. These are beyond the scope of our introduction to view controllers but you can find information about them in the UIViewController class reference.

That’s our look at the bare view controller. You now know not only how to create your first view controller but also how to use the fundamental methods and properties that you’ll find in every view controller. But the other types of view controller also have special possibilities all their own. We’ll look at these, starting with the one other view controller that’s intended to control a single page of data: the table view controller.

Next post:

Previous post: