From Code To Screen: how our code gets turned into pixels on the screen

Sophie Zhou
Nextdoor Engineering
5 min readJun 26, 2014

--

The Nextdoor mobile team recently encountered an interesting layout bug. While the solution to our problem ended up being a fairly simple one, it did lead us to do some deep diving into the way view controller and views coordinate their efforts to display dynamic content.

This is what was happening in our app:

We had a base view controller, we’ll call it RootViewController. Tapping a button brought up a view controller with a menu, we’ll call this MenuViewController. When one of its menu options was selected, MenuViewController would animate away and another view controller would be presented modally. We’ll call this ModalViewController. The problem that we were facing is that ModalViewController did not always show up.

To understand what’s going on here, let’s do a crash course on UIViewController, UIView, and how views ultimately get to appear on screen.

Views and View Controllers

UIView represents a rectangular area on the screen in which content can be displayed. At any given point in time in an application’s life, there are many UIViews on screen. A set of views that is visible at a time is generally managed by a UIViewController. A UIViewController is in charge of initially placing its views, and then correctly updating them in response to events. When its job is done, it will yield this responsibility to another UIViewController.

View Hierarchy

A view hierarchy is, in computer science terms, a tree of views. Each view can have as many child views (called subviews) as it wants, and each view has up to one parent view (called a superview). A node that has no parent view is the root of all the views in its view hierarchy. An application can have as many view hierarchies as it wants, but they will only be visible if its root view is a special type of view, called a UIWindow.

UIWindow

UIWindow is a UIView with special privileges. At most points of time, an iPhone application has only one UIWindow. (There are certain times when there are more UIWindows, but that’s outside the scope of this blog.) UIWindow has one magic function which connects the world of UIViews to content on the screen, and that is — (void)makeKeyAndVisible. At any given point in time, the UIWindow that has most recently received this message will be the view whose entire view hierarchy is displayed on screen.

Now, knowing what we do about UIViewControllers, UIViews, and UIWindows, let’s talk about the ways to get a view controller’s content on the screen. Generally, there are three ways:

1) Attach its view directly to UIWindow:

[window addSubview:viewController.view]

OR

window.rootViewController = viewController

2) A parent view controller attaches it. In this case, a parent view controller will give part (or possibly all) of the screen over to the child view controller, while it maintains control of the other part of the screen. In this case, two view controllers are “active” at the same time.

A classic example of this setup is UINavigationController. UINavigationController takes the rectangle in the upper part of the screen, and gives the bottom part of the screen to its child view controller:

3) Another view controller presents it. It is important to note that this is the not the same thing as having a parent view controller. In a parent-child view controller setup, both view controllers can have control over part of the screen. In a presentation, the presenting view controller yields the entire screen over to the new view controller, and the presenting view controller is not active while the presented view controller is. When the presented view controller is dismissed, the control of the entire screen is returned to the presenting view controller.

When presentations happen, the presenting and presented view controllers get references to each other, and this kind of relationship is formed (This image comes from Apple Documentation Presenting View Controllers from Other View Controllers).

Why Our ModalViewController Didn’t Appear

Now, let’s zoom back into our specific problem. A presented view controller (situation 3) was inexplicably not showing up. A quick refresher image:

Our MenuViewController is animating out at the same time as ModalViewController is being animated in. This is the line of code that we use to present the new view controller:

[menuViewController presentViewController:modalViewController animated:YES completion:nil];

Do you see the problem? MenuViewController is being animated away while it’s presenting another view controller. We now know that once a view controller presents another view controller, a presenter / presentee relationship is formed between the view controller. When the presented view controller is dismissed, the power will get returned to the presenting view controller. If this happened in our situation, things would get seriously broken. In our case, the runtime was actually kind enough to spit out a useful error message:

‘Warning: Attempt to present <ModalViewController: 0x1682df00> on <MenuViewController: 0x168a7a00> whose view is not in the window hierarchy!’

Solution

Our solution for this problem was to have a view controller that was still alive in the view controller chain present our new view controller. Specifically, we have the presenter of our MenuViewController also present the new ModalViewController. This is what that looked like:

[menuViewController.presentingViewController presentViewController:modalViewController animated:YES]
There were other ways we could have presented view controllers, but many of the other solutions would have had unexpected outcomes. Thinking back to the three ways we can display view controller content, we could have selected one of the other two presentation methods:

1) Attached directly to the UIWindow:

We should not do this for MenuViewController because it is not the first view controller to be attached to the screen, so there should already be a view controller available to either present it or parent it.

2) A parent view controller attaches it.

In our case, we did not want ModalViewController to have a UINavigationController, or any other type of parent view, so we did not use this approach.

Key Takeaway

If a view controller is going to attach a child view controller or present another view controller, make sure it stays around. In our case, MenuViewController presented a view controller but was animated away before our presented view controller had the chance to be animated in. As a result, our ModalViewController was not able to attach itself to the window hierarchy.

--

--