For the More Curious: View Controller Relationships

The relationships between view controllers are important for understanding where and how a view controller’s view appears on the screen. Overall, there are two different types of relationships between view controllers: parent-child relationships and presenting-presenter relationships. Let’s look at each one individually.

Parent-child relationships

Parent-child relationships are formed when using view controller containers. Examples of view controller containers are UINavigationController, UITabBarController, and UISplitViewController (which you will see in Chapter 22). You can identify a view controller container because it has a viewControllers property that is an array of the view controllers it contains.

A view controller container is always a subclass of UIViewController and thus has a view. The behavior of a view controller container is that it selectively adds the views of its viewControllers as subviews of its own view. A container has its own built-in interface, too. For example, a UINavigationController’s view shows a navigation bar and the view of its topViewController.

View controllers in a parent-child relationship form a family. So, a UINavigationController and its viewControllers are in the same family. A family can have multiple levels. For example, imagine a situation where a UITabBarController contains a UINavigationController that contains a UIViewController. These three view controllers are in the same family (Figure 17.5). The container classes have access to their children through the viewControllers array, and the children have access to their ancestors through four properties of UIViewController.

Figure 17.5  A view controller family

A view controller family

Every UIViewController has a parentViewController property. This property holds the closest view controller ancestor in the family. Thus, it could return a UINavigationController, UITabBarController, or a UISplitViewController depending on the makeup of the family tree.

The ancestor-access methods of UIViewController include navigationController, tabBarController, and splitViewController. When a view controller is sent one of these messages, it searches up the family tree (using the parentViewController property) until it finds the appropriate type of view controller container. If there is no ancestor of the appropriate type, these methods return nil.

Presenting-presenter relationships

The other kind of relationship is a presenting-presenter relationship, which occurs when a view controller is presented modally. When a view controller is presented modally, its view is added on top of the view controller’s view that presented it. This is different than a view controller container, which intentionally keeps a spot open on its interface to swap in the views of the view controllers it contains. Any UIViewController can present another view controller modally.

Figure 17.6  Presenting-presenter relationship

Presenting-presenter relationship

There are two built-in properties for managing the relationship between presenter and presentee. A modally-presented view controller’s presentingViewController will point back to the view controller that presented it, while the presenter will keep a pointer to the presentee in its presentedViewController property (Figure 17.6).

Inter-family relationships

A presented view controller and its presenter are not in the same view controller family. Instead, the presented view controller has its own family. Sometimes, this family is just one UIViewController; other times, this family is made up of multiple view controllers.

Understanding the difference in families will help you understand the values of properties like presentedViewController and navigationController. Consider the view controllers in Figure 17.7. There are two families, each with multiple view controllers. This diagram shows the values of the view controller relationship properties.

Figure 17.7  A view controller hierarchy

A view controller hierarchy

First, notice that the properties for parent-child relationships can never cross over family boundaries. Thus, sending tabBarController to a view controller in Family 2 will not return the UITabBarController in Family 1; it will return nil. Likewise, sending navigationController to the view controller in Family 2 returns its UINavigationController parent in Family 2 and not the UINavigationController in Family 1.

Perhaps the oddest view controller relationships are the ones between families. When a view controller is presented modally, the actual presenter is the oldest member of the presenting family. For example, in Figure 17.7, the UITabBarController is the presentingViewController for the view controllers in Family 2. It does not matter which view controller in Family 1 was sent presentViewController:animated:completion:, the UITabBarController is always the presenter.

This behavior explains why the BNRDetailViewController obscures the UINavigationBar when presented modally but not when presented normally in the UINavigationController’s stack. Even though the BNRItemsViewController is told to do the modal presenting, its oldest ancestor, the UINavigationController, actually carries out the task. The BNRDetailViewController is put on top of the UINavigationController’s view and thus obscures the UINavigationBar.

Notice also that the presentingViewController and presentedViewController are valid for every view controller in each family and always point to the oldest ancestor in the other family.

You can actually override this oldest-ancestor behavior (but only on the iPad). By doing so, you can specify where the views of the presented view controller family appear on the screen. For example, you could present the BNRDetailViewController and its navigationController so that it only obscures the UITableView but not the UINavigationBar.

Every UIViewController has a definesPresentationContext property for this purpose. By default, this property is NO, which means the view controller will always pass presentation off to its next ancestor, until there are no more ancestors left. Setting this property to YES interrupts the search for the oldest ancestor, allowing a view controller to present the modal view controller in its own view (Figure 17.8). Additionally, you must set the modalPresentationStyle for the presented view controller to UIModalPresentationCurrentContext.

Figure 17.8  Presentation context

Presentation context

You can test this out by changing the code in BNRItemsViewController.m’s addNewItem: method.

 ​ ​ ​ ​U​I​N​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​ ​*​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​=​ ​[​[​U​I​N​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​i​t​W​i​t​h​R​o​o​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​]​;​

 ​ ​ ​ ​n​a​v​C​o​n​t​r​o​l​l​e​r​.​m​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​S​t​y​l​e​ ​=​ ​U​I​M​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​F​o​r​m​S​h​e​e​t​;​
 ​ ​ ​ ​n​a​v​C​o​n​t​r​o​l​l​e​r​.​m​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​S​t​y​l​e​ ​=​ ​U​I​M​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​C​u​r​r​e​n​t​C​o​n​t​e​x​t​;​
 ​ ​ ​ ​s​e​l​f​.​d​e​f​i​n​e​s​P​r​e​s​e​n​t​a​t​i​o​n​C​o​n​t​e​x​t​ ​=​ ​Y​E​S​;​

 ​ ​ ​ ​n​a​v​C​o​n​t​r​o​l​l​e​r​.​m​o​d​a​l​T​r​a​n​s​i​t​i​o​n​S​t​y​l​e​ ​=​ ​U​I​M​o​d​a​l​T​r​a​n​s​i​t​i​o​n​S​t​y​l​e​F​l​i​p​H​o​r​i​z​o​n​t​a​l​;​

 ​ ​ ​ ​[​s​e​l​f​ ​p​r​e​s​e​n​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​a​n​i​m​a​t​e​d​:​Y​E​S​ ​c​o​m​p​l​e​t​i​o​n​:​n​i​l​]​;​
}​

After building and running on the iPad, tap the + icon. Notice that the BNRDetailViewController does not obscure the UINavigationBar. Make sure you undo this code before moving on to the next chapter.

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

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