Creating Custom Content View Controllers

Custom content view controllers are the heart of your app. You use them to present your app’s unique content. All apps need at least one custom content view controller. Complex apps divide the workload between multiple content controllers.

A view controller has many responsibilities. Some of these responsibilities are things that iOS requires the view controller to do. Other responsibilities are things you assign to the view controller when you define its role in your app.

Anatomy of a Content View Controller

The UIViewController class provides the fundamental infrastructure for implementing all custom view controllers. You define a custom subclass of UIViewController. That subclass provides the necessary code to populate views with data and respond to user actions. When you want to make adjustments to the default behavior of the view controller, you override methods of the UIViewController class. Your view controller may also interact with other UIKit classes to implement the behavior you want.

Figure 3-1 shows some of the key objects associated directly with a content view controller. These are the objects that are essentially owned and managed by the view controller itself. The view (accessible via the view property) is the only object that must be provided, although most view controllers have additional subviews attached to this view as well as custom objects containing the data they need to display.

Figure 3-1  Anatomy of a content view controller

When you design a new view controller, it potentially has many responsibilities. Some of those responsibilities look inward, to the views and other objects it controls. Other responsibilities look outward to other controllers. The following sections enumerate many of the common responsibilities for a view controller.

View Controllers Manage Resources

Some objects are instantiated when the view controller is initialized and are disposed of when your view controller is released. Other objects, like views, are needed only when the view controller’s contents are visible onscreen. As a result, view controllers use resources efficiently and should be prepared to release resources when memory is scarce. Properly implementing this behavior in your app's view controllers makes it so your app uses memory and other resources—such as CPU, GPU, and battery—more efficiently.

See “Resource Management in View Controllers.”

View Controllers Manage Views

View controllers manage their view and its subviews, but the view’s frame—its position and size in its parent’s view—is often determined by other factors, including the orientation of the device, whether or not the status bar is visible and even how the view controller’s view is displayed in the window. Your view controller should be designed to layout its view to fit the frame provided to it.

View management has other aspects as well. Your view controller is notified when its view is about to appear and disappear from the screen. Your view controller can use this notification to perform other actions necessary to its operation.

See “Resizing the View Controller’s Views,” “Supporting Multiple Interface Orientations,” “Responding to Display-Related Notifications.”

View Controllers Respond to Events

Your view controller is often the central coordinating object for its views and controls. Typically, you design your user interface so that controls send messages to the controller when a user manipulates them. Your view controller is expected to handle the message, making any necessary changes to the views or data stored in the view controller.

Your view controller also participates in the responder chain used to deliver events to your app. You can override methods in your view controller class to have it participate directly in event handling. View controllers also are good objects to implement other behaviors—such as responding to system notifications, timers or events specific to your app.

See “Using View Controllers in the Responder Chain.”

View Controllers Coordinate with Other Controllers

Although a view controller may create and manage many other objects, it does not usually need to expose these objects publicly to inspection or modification. It may collaborate with other objects (especially other view controllers), but it should expose the fewest number of properties and methods necessary to allow its collaborators to communicate with it. Exposing too many implementation details in your view controller class makes it difficult to modify your view controller’s implementation. Collaborators that rely on these implementation details would need to be modified to continue to work with your view controller class.

See “Coordinating Efforts Between View Controllers.”

View Controllers Often Work with Containers

If your view controller is placed inside a container view controller, the container imposes additional constraints, as shown in Figure 3-2. The container may ask your view controller to provide other objects used to configure the container’s user interface. For example, a content view controller placed inside a tab view controller provides a tab bar item to display for that tab.

Figure 3-2  A container view controller imposes additional demands on its children

The properties used to configure the containers provided by UIKit are defined by the UIViewController class. For more information on specific types of containers and the properties you configure to support them, see View Controller Catalog for iOS.

View Controllers May Be Presented by Other View Controllers

Some view controllers you design are intended to be presented by other view controllers. You might present your view controller directly, or you might make it a child of a container view controller and present the container instead. When presented, it moves onscreen, remaining there until it is dismissed.

There are several reasons you might present a view controller:

  • To gather information from the user immediately.

  • To present some content temporarily.

  • To change work modes temporarily.

  • To implement alternate interfaces for different device orientations.

  • To present a new view hierarchy with a specific type of animated transition (or no transition).

Most of these reasons involve interrupting your app’s workflow temporarily in order to gather or display some information. In almost all cases, the presented view controller implements a delegate. The presented view controller uses the delegate to communicate with the presenting view controller. After your app has the information it needs (or the user finishes viewing the presented information), the presented view controller communicates this back to the presenting view controller. The presenting view controller dismisses the presented view controller to return the app to its previous state.

See “Presenting View Controllers from Other View Controllers.”

Designing Your Content View Controller

Before writing any code in your view controller, you should be able to answer some basic questions about how you intend to use it. The questions provided below are designed to help you narrow the focus of your view controller and to help you understand the role it plays in your app. In particular, it helps you identify connections—usually to other controllers—your view controller needs to perform its tasks.

Your answers to these questions need not be precise if you are still working out the role it plays. Still, it helps to have a general sense of what your view controller does and how other objects interact with it.

The questions above don’t ask you to define the appearance of your view controller or to be precise about the implementation details of how it performs the tasks you’ve assigned to it. Those are important questions you need to answer, but neither of these things should affect your view controller’s public interface. You want the flexibility to be able to change the visual design of your view controller without changing the class declaration that defines how other controllers collaborate with it.

Use a Storyboard to Implement Your View Controller

You might consider whether or not to use a storyboard as an implementation detail, but the approach you take affects how you implement the view controller and how other objects collaborate with it. You always use a storyboard unless you have a strong reason not to.

When you use storyboards:

  • iOS usually instantiates your view controller for you automatically.

  • To finish instantiating it, you override its awakeFromNib method.

  • Other objects configure it through its properties.

  • You create its view hierarchy and other related objects in Interface Builder. These objects are loaded automatically when the view is needed.

  • Relationships with other view controllers are created in the storyboard.

If you design your view controller to be used programmatically:

  • The view controller is instantiated by allocating and initializing it.

  • You create an custom initialization method to initialize the view controller.

  • Other objects configure the view controller using its initialization method and by configuring its properties.

  • You override the loadView method to programmatically create and configure its view hierarchy.

  • Relationships with other view controllers are created by writing code.

Know When Your Controller Is Instantiated

Knowing when your view controller is instantiated usually implies other details for how your app operates. For example, you might know that your view controller is always instantiated by the same object. Often the objects that instantiate view controllers are themselves view controllers; this is almost always the case in an app that uses storyboards. In any case, knowing when, why, and by what object your view controller is instantiated gives you insight into the information exchanged between your view controller and the object that created it.

Know What Data Your View Controller Shows and Returns

When you answer these two questions, you are working to understand the data model for your app and also whether that data needs to be exchanged between your view controllers.

Here are some common patterns you should expect to see in your view controllers:

  • The view controller receives data from another controller and displays it, without offering a way to edit it. No data is returned.

  • The view controller allows the user to enter new data. After the user finishes editing the data, it sends the new data to another controller.

  • The view controller receives data from another controller and allows the user to edit it. After the user finishes editing the data, it sends the new data to another controller.

  • The view controller doesn’t send or receive data. Instead, it shows static views.

  • The view controller doesn’t send or receive data. Instead, its implementation loads its data without exposing this mechanism to other view controllers. For example, the GKAchievementViewController class has built-in functionality to determine which player is authenticated on the device. It also knows how to load that player’s data from Game Center. The presenting view controller does not need to know what data is loaded or how it was loaded.

You are not constrained to use only these designs.

When data travels into or out of your view controller, consider defining a data model class to hold the data to be transferred to the new controller. For example, in Your Second iOS App: Storyboards, the master controller uses a BirdSighting object to send data associated with a sighting to the detail controller. Using an object for this makes it easier to update the data to include additional properties without changing the method signatures in your controller classes.

Know What Tasks Your Controller Allows the User to Perform

Some view controllers allow users to view, create, or edit data. Other view controllers allow users to navigate to other screens of content. And some allow users to perform tasks provided by the view controller. For example, the MFMailComposeViewController class allows a user to compose and send emails to other users. It handles the low-level details of sending mail messages.

As you determine which tasks your view controller performs, decide how much control over those tasks your view controller exposes to other controllers. Many view controllers can perform tasks without exposing configuration data to other controllers. For example, the GKAchievementViewController class displays achievements to the user without exposing any properties to configure how it loads or presents its data. The MFMailComposeViewController class presents a slightly different scenario by exposing some properties that another controller can use to configure the initial content it displays. After that, a user can edit the content and send the email message without giving other controller objects a chance to affect that process.

Know How Your View Controller Is Displayed Onscreen

Some view controllers are designed to be root view controllers. Others expect to be presented by another view controller or placed in a container controller. Occasionally, you design controllers that can be displayed in multiple ways. For example, a split view controller’s master view is displayed in the split view in landscape mode and in a popover control in portrait mode.

Knowing how your view controller is displayed gives you insight into how its view is sized and placed onscreen. It also affects other areas, such as determining what other controllers your view controller collaborates with.

Know How Your Controller Collaborates with Other Controllers

By this point, you already know some things about collaboration. For example, if your view controller is instantiated from a segue, then it probably collaborates with the source view controller that configures it. And if your controller is a child of a container, then it collaborates with the container. But there are relationships in the other direction as well. For example, your view controller might defer some of its work and hand it off to another view controller. It might even exchange data with an existing view controller.

With all of these connections, your view controller provides an interface that other controllers use, or it is aware of other controllers and it uses their interfaces. These connections are essential to providing a seamless experience, but they also represent design challenges because they introduce dependencies between classes in your app. Dependencies are a problem because they make it more difficult to change any one class in isolation from the other classes that make up your app. For this reason, you need to balance the needs of your app now against the potential need to keep your app design flexible enough to change later.

Examples of Common View Controller Designs

Designing a new view controller can be challenging. It helps to look at existing designs and understand what they do and why. This next section talks about some common view controller styles used in iOS apps. Each example includes a description of the role the view controller plays, a brief description of how it works at a high level, and one possible list of answers to the design questions listed above.

Example: Game Title Screen

Mission Statement

A view controller that allows the user to select between different styles of game play.

Description

When a game is launched, it rarely jumps right into the actual game. Instead, it displays a title screen that identifies the game and presents a set of game variants to the player. For example, a game might offer buttons that allow a player to start a single player or multiplayer game. When the user selects one of the options, the app configures itself appropriately and launches into its gameplay.

A title screen is interesting specifically because its contents are static; they don’t need data from another controller. As such, this view controller is almost entirely self-sufficient. It does, however, know about other view controllers, because it instantiates other view controllers to launch its gameplay.

Design

  • Are you using a storyboard to implement the view controller? Yes.

  • When is it instantiated? This view controller is the initial scene in the main storyboard.

  • What data does it show? This class displays preconfigured controls and images; it does not present user data. It does not include configurable properties.

  • What tasks does it perform? When a user taps on a button, it triggers a segue to instantiate another view controller. Each segue is identified so that the appropriate game play can be configured.

  • How is its view displayed onscreen? It is installed automatically as the root view controller of the window.

  • How does it collaborate with other view controllers? It instantiates another view controller to present a gameplay screen. When gameplay ends, the other view controller sends a message to the title screen controller to inform it that play has ended. The title screen controller then dismisses the other view controller.

Alternative Design Considerations

The default answers assume that no user data is displayed. Some games include player data to configure the views or controls. For example:

  • You might want the view controller to display the user’s Game Center alias.

  • You might want it to enable or disable buttons based on whether the device is connected to Game Center.

  • You might want it to enable or disable buttons based on In-App Purchase items the user has purchased.

When these additional items are added to the design, the view controller takes on a more traditional role. It might receive data objects or data controllers from the app delegate so that it can query and update this state as necessary. Or, as it is the root view controller for the window, you might simply implement those behaviors directly in the title screen controller. The actual design likely depends on how flexible you need your code to be.

Example: Master View Controller

Mission Statement

The initial view controller of a navigation controller, used to display a list of the app’s available data objects.

Description

A master view controller is a very common part of a navigation-based app. For example, Your Second iOS App: Storyboards uses a master view to display the list of bird sightings. When a user selects a sighting from the list, the master view controller pushes a new detail controller onto the screen.

Because this view controller displays a list of items, it subclasses UITableViewController instead of UIViewController.

Design

  • Are you using a storyboard to implement the view controller? Yes.

  • When is it instantiated? As the root view controller of a navigation controller, it is instantiated at the same time as its parent.

  • What data does it show? A high-level view of the app’s data. It implements properties that the app delegate uses to provide data to it. For example, the bird watching app provides a custom data controller object to the master view controller.

  • What tasks does it perform? It implements an Add button to allow users to create new records.

  • How is its view displayed onscreen? It is a child of a navigation controller.

  • How does it collaborate with other view controllers? When the user taps on an item in the list, it uses a push segue to show a detail controller. When the user taps on the Add button, it uses a modal segue to present a new view controller that edits a new record. It receives data back from this modal view controller and sends this data to the data controller to create a new bird sighting.

Alternative Design Considerations

A navigation controller and an initial view controller is used when building an iPhone app. When designing the same app for the iPad, the master view controller is a child of a split view controller instead. Most other design decisions stay the same.

Example: Detail View Controller

Mission Statement

A controller pushed onto a navigation stack to display the details for a list item selected from the master view controller.

Description

The detail view controller represents a more detailed view of a list item displayed by the master view controller. As with the master view controller, the list appears inside a navigation bar interface. When the user finishes viewing the item they click a button in the navigation bar to return to the master view.

Your Second iOS App: Storyboards uses the UITableViewController class to implement its detail view. It uses a static table cells, each of which accesses one piece of the bird sighting data. A static table view is a good way to implement this design.

Design

  • Are you using a storyboard to implement the view controller? Yes.

  • When is it instantiated? It is instantiated by a push segue from the master view controller.

  • What data does it show? It shows the data stored in a custom data object. It declares properties configured by the source view controller to provide this data.

  • What tasks does it perform? It allows the user to dismiss the view.

  • How is its view displayed onscreen? It is a child of a navigation controller.

  • How does it collaborate with other view controllers? It receives data from the master view controller.

Alternative Design Considerations

A navigation controller is most often used when building an iPhone app. When designing the same app for the iPad, the detail view controller is a child of a split view controller instead. Many of the other implementation details stay the same.

If your app needs more custom view behavior, it might subclass the UIViewController class and implement its own custom view hierarchy.

Example: Mail Compose View Controller

Mission Statement

A view controller that allows the user to compose and send an email.

Description

The Message UI framework provides the MFMailComposeViewController class. This class allows a user to compose and send an email. This view controller is interesting because it does more than simply show or edit data—it actually sends the email.

Another interesting design choice in this class is that it allows an app to provide an initial configuration for the email message. After the initial configuration has been presented, the user can override these choices before sending the mail.

Design

  • Are you using a storyboard to implement the view controller? No.

  • When is it instantiated? It is instantiated programmatically.

  • What data does it show? It shows the various parts of an email message, including a recipients list, title, attachments and the email message itself. The class provides properties that allow another view controller to preconfigure the email message.

  • What tasks does it perform? It sends email.

  • How is its view displayed onscreen? The view controller is presented by another view controller.

  • How does it collaborate with other view controllers? It returns status information to its delegate. This status allows the presenting view controller to know whether an email was sent.

Implementation Checklist for Custom Content View Controllers

For any custom content view controllers you create, there are a few tasks that you must have your view controller handle:

As you implement your view controller, you will likely discover that you need to define action methods or outlets to use with its views. For example, if the view hierarchy contains a table, you probably want to store a pointer to that table in an outlet so that you can access it later. Similarly, if your view hierarchy contains buttons or other controls, you probably want those controls to call an associated action method on the view controller. As you iterate through the definition of your view controller class, you may therefore find that you need to add the following items to your view controller class: