View Controller Basics
Apps running on iOS–based devices have a limited amount of screen space for displaying content and therefore must be creative in how they present information to the user. Apps that have lots of information to display must therefore only show a portion to start, and then show and hide additional content as the user interacts with the app. View controller objects provide the infrastructure for managing content and for coordinating the showing and hiding of it. By having different view controller classes control separate portions of your user interface, you break up the implementation of your user interface into smaller and more manageable units.
Before you can use view controllers in your app, you need a basic understanding of the major classes used to display content in an iOS app, including windows and views. A key part of any view controller’s implementation is to manage the views used to display its content. However, managing views is not the only job view controllers perform. Most view controllers also communicate and coordinate with other view controllers when transitions occur. Because of the many connections view controllers manage, both looking inward to views and associated objects and looking outward to other controllers, understanding the connections between objects can sometimes be difficult. Instead, use Interface Builder to create storyboards. Storyboards make it easier to visualize the relationships in your app and greatly simplify the effort needed to initialize objects at runtime.
Screens, Windows, and Views Create Visual Interfaces
Figure 1-1 shows a simple interface. On the left, you can see the objects that make up this interface and understand how they are connected to each other.
There are three major objects at work here:
UIScreenobject that identifies a physical screen connected to the device.
UIWindowobject that provides drawing support for the screen.
A set of
UIViewobjects to perform the drawing. These objects are attached to the window and draw their contents when the window asks them to.
Figure 1-2 shows how these classes (and related important classes) are defined in UIKit.
Although you don’t need to understand everything about views to understand view controllers, it can be helpful to consider the most salient features of views:
A view represents a user interface element. Each view covers a specific area. Within that area, it displays contents or responds to user events.
Views can be nested in a view hierarchy. Subviews are positioned and drawn relative to their superview. Thus, when the superview moves, its subviews move with it. This hierarchy makes it easy to assemble a group of related views by placing them in a common superview.
Views can animate their property values. When a change to a property value is animated, the value gradually changes over a defined period of time until it reaches the new value. Changes to multiple properties across multiple views can be coordinated in a single animation.
Animation is critically important to iOS app development. Because most apps display only a portion of their contents at one time, an animation allows the user to see when a transition occurred and where the new content came from. An instantaneous transition might confuse the user.
Views rarely understand the role they play in your app. For example, Figure 1-1 shows a button (titled Hello), which is a special kind of view, known as a control. Controls know how to respond to user interaction in their area, but they don’t know what they control. Instead, when a user interacts with a control, it sends messages to other objects in your app. This flexibility allows a single class (
UIButton) to provide the implementation for multiple buttons, each configured to trigger a different action.
A complex app needs many views, often assembling them into view hierarchies. It needs to animate subsets of these views onto or off the screen to provide the illusion of a single larger interface. And finally, to keep view classes reusable, the view classes need to be ignorant of the specific role they perform in the app. So the app logic—the brains—needs to be placed somewhere else. Your view controllers are the brains that tie your app’s views together.
View Controllers Manage Views
Each view controller organizes and controls a view; this view is often the root view of a view hierarchy. View controllers are controller objects in the MVC pattern, but a view controller also has specific tasks iOS expects it to perform. These tasks are defined by the
UIViewController class that all view controllers inherit from. All view controllers perform view and resource management tasks; other responsibilities depend on how the view controller is used.
Figure 1-3 shows the interface from Figure 1-1, but updated here to use a view controller. You never directly assign the views to the window. Instead, you assign a view controller to the window, and the view controller automatically adds its view to the window.
A view controller is careful to load its view only when the view is needed. It can also release the view under certain conditions. For these reasons, view controllers play a key part in managing resources in your app.
A view controller is the natural place to coordinate actions of its connected views. For example, when a button is pressed, it sends a message to the view controller. Although the view itself may be ignorant of the task it performs, the view controller is expected to understand what the button press means and how it should respond. The controller might update data objects, animate or change property values stored in its views, or even bring another view controller’s contents to the screen.
Usually, each view controller instantiated by your app sees only a subset of your app’s data. It knows how to display that particular set of data, without needing to know about other kinds of data. Thus, an app’s data model, user interface design, and the view controllers you create are all influenced by each other.
Figure 1-4 shows an example of an app that manages recipes. This app displays three related but distinct views. The first view lists the recipes that the app manages. Tapping a recipe shows the second view, which describes the recipe. Tapping the recipe’s picture in the detail view shows the third view, a larger version of the photo. Each view is managed by a distinct view controller object whose job is to present the appropriate view, populate the subviews with data, and respond to user interactions within the view hierarchy.
This example demonstrates a few factors common to view controllers:
Every view is controlled by only one view controller. When a view is assigned to the view controller’s
viewproperty, the view controller owns it. If the view is a subview, it might be controlled by the same view controller or a different view controller. You’ll learn more about how to use multiple view controllers to organize a single view hierarchy when you learn about container view controllers.
Each view controller interacts with a subset of your app’s data. For example, the Photo controller needs to know only the photo to be displayed.
Because each view controller provides only a subset of the user experience, the view controllers must communicate with each other to make this experience seamless. They may also communicate with other controllers, such as data controllers or document objects.
A Taxonomy of View Controllers
Figure 1-5 shows the view controller classes available in the UIKit framework along with other classes important to view controllers. For example, the
UITabBarController object manages a
UITabBar object, which actually displays the tabs associated with the tab bar interface. Other frameworks define additional view controller classes not shown in this figure.
View controllers, both those provided by iOS and those you define, can be divided into two general categories—content view controllers and container view controllers—which reflect the role the view controller plays in an app.
Content View Controllers Display Content
A content view controller presents content on the screen using a view or a group of views organized into a view hierarchy. The controllers described up to this point have been content view controllers. A content view controller usually knows about the subset of the app’s data that is relevant to the role the controller plays in the app.
Here are common examples where your app uses content view controllers:
To show data to the user
To collect data from the user
To perform a specific task
To navigate between a set of available commands or options, such as on the launch screen for a game
Content view controllers are the primary coordinating objects for your app because they know the specific details of the data and tasks your app offers the user.
Each content view controller object you create is responsible for managing all the views in a single view hierarchy. The one-to-one correspondence between a view controller and the views in its view hierarchy is the key design consideration. You should not use multiple content view controllers to manage the same view hierarchy. Similarly, you should not use a single content view controller object to manage multiple screens’ worth of content.
For information about defining your content view controller and implementing the required behaviors, see “Creating Custom Content View Controllers.”
About Table View Controllers
Many apps display tabular data. For this reason, iOS provides a built-in subclass of the
UIViewController class designed specifically for managing tabular data. This class,
UITableViewController, manages a table view and adds support for many standard table-related behaviors such as selection management, row editing, and table configuration. This additional support is there to minimize the amount of code you must write to create and initialize a table-based interface. You can also subclass
UITableViewController to add other custom behaviors.
Figure 1-6 shows an example using a table view controller. Because it is a subclass of the
UIViewController class, the table view controller still has a pointer to the root view of the interface (through its
view property) but it also has a separate pointer to the table view displayed in that interface.
For more information about table views, see Table View Programming Guide for iOS.
Container View Controllers Arrange Content of Other View Controllers
A container view controller contains content owned by other view controllers. These other view controllers are explicitly assigned to the container view controller as its children. A container controller can be both a parent to other controllers and a child of another container. Ultimately, this combination of controllers establishes a view controller hierarchy.
Each type of container view controller establishes a user interface that its children operate in. The visual presentation of this user interface and the design it imposes on its children can vary widely between different types of containers. For example, here are some ways that different container view controllers may distinguish themselves:
A container provides its own API to manage its children.
A container decides whether the children have a relationship between them and what that relationship is.
A container manages a view hierarchy just as other view controllers do. A container can also add the views of any of its children into its view hierarchy. The container decides when such a view is added and how it should be sized to fit the container’s view hierarchy, but otherwise the child view controller remains responsible for the view and its subviews.
A container might impose specific design considerations on its children. For example, a container might limit its children to certain view controller classes, or it might expect those controllers to provide additional content needed to configure the container’s views.
The built-in container classes are each organized around an important user interface principle. You use the user interfaces managed by these containers to organize complex apps.
About Navigation Controllers
A navigation controller presents data that is organized hierarchically and is an instance of the
UINavigationController class. The methods of this class provide support for managing a stack-based collection of content view controllers. This stack represents the path taken by the user through the hierarchical data, with the bottom of the stack reflecting the starting point and the top of the stack reflecting the user’s current position in the data.
Figure 1-7 shows screens from the Contacts app, which uses a navigation controller to present contact information to the user. The navigation bar at the top of each page is owned by the navigation controller. The rest of each screen displayed to the user is managed by a content view controller that presents the information at that specific level of the data hierarchy. As the user interacts with controls in the interface, those controls tell the navigation controller to display the next view controller in the sequence or dismiss the current view controller.
Although a navigation controller’s primary job is to manage its child view controllers, it also manages a few views. Specifically, it manages a navigation bar (that displays information about the user’s current location in the data hierarchy), a button (for navigating back to previous screens), and any custom controls the current view controller needs. You do not directly modify the views owned by the view controller. Instead, you configure the controls that the navigation controller displays by setting properties on each child view controller.
For information about how to configure and use navigation controller objects, see “Navigation Controllers”.
About Tab Bar Controllers
A tab bar controller is a container view controller that you use to divide your app into two or more distinct modes of operation. A tab bar controller is an instance of the
UITabBarController class. The tab bar has multiple tabs, each represented by a child view controller. Selecting a tab causes the tab bar controller to display the associated view controller’s view on the screen.
Figure 1-8 shows several modes of the Clock app along with the relationships between the corresponding view controllers. Each mode has a content view controller to manage the main content area. In the case of the Clock app, the Clock and Alarm view controllers both display a navigation-style interface to accommodate some additional controls along the top of the screen. The other modes use content view controllers to present a single screen.
You use tab bar controllers when your app either presents different types of data or presents the same data in different ways.
For information about how to configure and use a tab bar controller, see “Tab Bar Controllers”.
About Split View Controllers
A split view controller divides the screen into multiple parts, each of which can be updated separately. The appearance of a split view controller may vary depending on its orientation. A split view controller is an instance of the
UISplitViewController class. The contents of a split view interface are derived from two child view controllers.
Figure 1-9 shows a split view interface from the MultipleDetailViews sample app. In portrait mode, only the detail view is displayed. The list view is made available using a popover. However, when displayed in landscape mode, the split view controller displays the contents of both children side by side.
Split view controllers are supported on iPad only and are designed to help you take advantage of the larger screen of that device. They are the preferred way to implement master-detail interfaces in iPad apps.
For information about how to configure and use a split view controller, see “Popovers”.
About Popover Controllers
Look again at Figure 1-9. When the split view controller is displayed in portrait mode, the master views is displayed in a special control, known as a popover. In an iPad app, you can use popover controllers (
UIPopoverController) to implement popovers in your own app.
A popover controller is not actually a container; it does not inherent from
UIViewController at all. But, in practice, a popover controller is similar to a container, so you apply the same programming principles when you use them.
For information about how to configure and use a popover controller, see “Popovers”.
About Page View Controllers
A page view controller is a container view controller used to implement a page layout. That layout allows users to flip between discrete pages of content as if it were a book. A page view controller is an instance of the
UIPageViewController class. Each content page is provided by a content view controller. The page view controller manages the transitions between pages. When new pages are required, the page view controller calls an associated data source to retrieve a view controller for the next page.
For information about how to configure and use a page view controller, see “Page View Controllers”.
A View Controller’s Content Can Be Displayed in Many Ways
For a view controller’s contents to be visible to the user, it must be associated with a window. There are many ways you can do this in your app:
Make the view controller a window’s root view controller.
Make the view controller a child of a container.
Show the view controller in a popover control.
Present it from another view controller.
Figure 1-10 shows an example from the Contacts app. When the user clicks the plus button to add a new contact, the Contacts view controller presents the New Contact view controller. The New Contact screen remains visible until the user cancels the operation or provides enough information about the contact that it can be saved to the contacts database. At that point the information is transmitted to the Contacts view controller, which then dismisses the controller it presented.
A presented view controller isn’t a specific type of view controller—the presented view controller can be either a content or a container view controller with an attached content view controller. In practice, the content view controller is designed specifically to be presented by another controller, so it can be useful to think of it as a variant of a content view controller. Although container view controllers define specific relationships between the managed view controllers, using presentation allows you to define the relationship between the view controller being presented and the view controller presenting it.
Most of the time, you present view controllers to gather information from the user or capture the user’s attention for some specific purpose. Once that purpose is completed, the presenting view controller dismisses the presented view controller and returns to the standard app interface.
It is worth noting that a presented view controller can itself present another view controller. This ability to chain view controllers together can be useful when you need to perform several modal actions sequentially. For example, if the user taps the Add Photo button in the New Contact screen in Figure 1-10 and wants to choose an existing image, the New Contact view controller presents an image picker interface. The user must dismiss the image picker screen and then dismiss the New Contact screen separately to return to the list of contacts.
When presenting a view controller, one view controller determines how much of the screen is used to present the view controller. The portion of the screen is called the presentation context By default, the presentation context is defined to cover the window.
For more information about how to present view controllers in your app, see “Presenting View Controllers from Other View Controllers.”
View Controllers Work Together to Create an App’s Interface
View controllers manage their views and other associated objects, but they also work with other view controllers to provide a seamless user interface. The distribution of work and communication between your app’s view controllers is an essential part of working with them. Because these relationships are so important to building complex apps, this next section reviews the relationships already discussed and describes them in more detail.
Parent-Child Relationships Represent Containment
A view controller hierarchy starts with a single parent, the root view controller of a window. If that view controller is a container, it may have children that provide content. Those controllers, in turn, may also be containers with children of their own. Figure 1-11 shows an example of a view controller hierarchy. The root view controller is a tab view controller with four tabs. The first tab uses a navigation controller with children of its own and the other three tabs are managed by content view controllers with no children.
The area each view controller fills is determined by its parent. The root view controller’s area is determined by the window. In Figure 1-11, the tab view controller gets its size from the window. It reserves space for its tab bar and gives the remainder of the space to its children. If the navigation controller were the control displayed right now, it reserves space for its navigation bar and hands the rest to its content controller. At each step, the child view controller’s view is resized by the parent and placed into the parent’s view hierarchy.
This combination of views and view controllers also establishes the responder chain for events handled by your app.
Sibling Relationships Represent Peers Inside a Container
The kind of container defines the relationships (if any exists) shared by its children. For example, compare the tab view controller and navigation controller.
In a tab view controller, the tabs represent distinct screens of content; tab bar controllers do not define a relationship between its children, although your app can choose to do so.
In a navigation controller, siblings display related views arranged in a stack. Siblings usually share a connection with adjacent siblings.
Figure 1-12 shows a common configuration of view controllers associated with a navigation controller. The first child, the master, shows the available content without showing all of the details. When an item is selected, it pushes a new sibling onto the navigation controller so that the user can see the additional details. Similarly, if the user needs to see more details, this sibling can push another view controller that shows the most detailed content available. When siblings have a well defined relationship as in this example, they often coordinate with each other, either directly or through the container controller. See Figure 1-15.
Presentation Represents a Transient Display of Another Interface
A view controller presents another view controller when it wants that view controller to perform a task. The presenting view controller is in charge of this behavior. It configures the presented view controller, receives information from it, and eventually dismisses it. However, while it is being presented, the presented view controller’s view is temporarily added to the window’s view hierarchy.
In Figure 1-13, a content view controller attached to the tab view presents a view controller to perform a task. The content controller is the presenting view controller, and the modal view controller is the presented view controller.
When a view controller is presented, the portion of the screen that it covers is defined by a presentation context provided to it by another view controller. The view controller that provides the presentation context does not need be the same view controller that presented it. Figure 1-14 shows the same view controller hierarchy that is presented in Figure 1-13. You can see that the content view presented the view controller, but it did not provide the presentation context. Instead, the view controller was presented by the tab controller. Because of this, even though the presenting view controller only covers the portion of the screen provided to it by the tab view controller, the presented view controller uses the entire area owned by the tab view controller.
Control Flow Represents Overall Coordination Between Content Controllers
In an app with multiple view controllers, view controllers are usually created and destroyed throughout the lifetime of the app. During their lifetimes, the view controllers communicate with each other to present a seamless user experience. These relationships represent the control flow of your app.
Most commonly, this control flow happens when a new view controller is instantiated. Usually, a view controller is instantiated because of actions in another view controller. The first view controller, known as the source view controller directs the second view controller, the destination view controller. If the destination view controller presents data to the user, the source view controller usually provides that data. Similarly, if the source view controller needs information from the destination view controller, it is responsible for establishing the connection between the two view controllers.
Figure 1-15 shows the most common examples of these relationships.
In the figure:
A child of a navigation controller pushes another child onto the navigation stack.
A view controller presents another view controller.
A view controller displays another view controller in a popover.
Each controller is configured by the one preceding it. When multiple controllers work together, they establish a communication chain throughout the app.
The control flow at each link in this chain is defined by the destination view controller. The source view controller uses the conventions provided by the destination view controller.
The destination view controller provides properties used to configure its data and presentation.
If the destination view controller needs to communicate with view controllers preceding it in the chain, it uses delegation. When the source view controller configures the destination view controller’s other properties, it is also expected to provide an object that implements the delegate’s protocol.
The benefit of using this control flow convention is that there is a clean division of responsibilities between each pair of source and destination view controllers. Data flows down the path when the source view controller asks the destination view controller to perform a task; the source view controller drives the process. For example, it might provide the specific data object that the destination controller should display. In the other direction, data flows up the path when a view controller needs to communicates information back to the source controller that spawned it. For example, it might communicate when the task completes.
Also, by consistently implementing this control flow model, you ensure that destination view controllers never know too much about the source view controller that configured them. When it does know about a view controller earlier in the chain, it knows only that the class implements the delegate protocol, not the class of the class. By keeping view controllers from knowing too much about each other, individual controllers become more reusable. For someone reading your code, a consistently implemented control flow model makes it easy to see the communication path between any pair of controllers.
Storyboards Help You Design Your User Interface
When you implement your app using storyboards, you use Interface Builder to organize your app’s view controllers and any associated views. Figure 1-16 shows an example interface layout from Interface Builder. The visual layout of Interface Builder allows you to understand the flow through your app at a glance. You can see what view controllers are instantiated by your app and their order of instantiation. But more than that, you can configure complex collections of views and other objects in the storyboard. The resulting storyboard is stored as a file in your project. When you build your project, the storyboards in your project are processed and copied into the app bundle, where they are loaded by your app at runtime.
Often, iOS can automatically instantiate the view controllers in your storyboard at the moment they are needed. Similarly, the view hierarchy associated with each controller is automatically loaded when it needs to be displayed. Both view controllers and views are instantiated with the same attributes you configured in Interface Builder. Because most of this behavior is automated for you, it greatly simplifies the work required to use view controllers in your app.
The full details of creating storyboards are described in Xcode Overview. For now, you need to know some of the essential terminology used when implementing storyboards in your app.
A scene represents an onscreen content area that is managed by a view controller. You can think of a scene as a view controller and its associated view hierarchy.
You create relationships between scenes in the same storyboard. Relationships are expressed visually in a storyboard as a connection arrow from one scene to another. Interface Builder usually infers the details of a new relationship automatically when you make a connection between two objects. Two important kinds of relationships exist:
Containment represents a parent-child relationship between two scenes. View controllers contained in other view controllers are instantiated when their parent controller is instantiated. For example, the first connection from a navigation controller to another scene defines the first view controller pushed onto the navigation stack. This controller is automatically instantiated when the navigation controller is instantiated.
An advantage to using containment relationships in a storyboard is that Interface Builder can adjust the appearance of the child view controller to reflect the presence of its ancestors. This allows Interface Builder to display the content view controller as it appears in your final app.
A segue represents a visual transition from one scene to another. At runtime, segues can be triggered by various actions. When a segue is triggered, it causes a new view controller to be instantiated and transitioned onscreen.
Although a segue is always from one view controller to another, sometimes a third object can be involved in the process. This object actually triggers the segue. For example, if you make a connection from a button in the source view controller’s view hierarchy to the destination view controller, when the user taps the button, the segue is triggered. When a segue is made directly from the source view controller to the destination view controller, it usually represents a segue you intend to trigger programatically.
Different kinds of segues provide the common transitions needed between two different view controllers:
A push segue pushes the destination view controller onto a navigation controller’s stack.
A modal segue presents the destination view controller.
A popover segue displays the destination view controller in a popover.
A custom segue allows you to design your own transition to display the destination view controller.
Segues share a common programming model. In this model, the destination controller is instantiated automatically by iOS and then the source view controller is called to configure it. This behavior matches the control flow model described in “Control Flow Represents Overall Coordination Between Content Controllers.”
You can also create connections between a view controller and objects stored in the same scene. These outlets and actions enable you to carefully define the relationships between the view controller and its associated objects. Connections are not normally visible in the storyboard itself but can be viewed in the Connections Inspector of Interface Builder.