The Life Cycle of a View

A Tale Told in Methods

When you think of an iPhone or an iPad, chances are the first thing you imagine is what you see on the screen. In an app, a view is what appears on said screen, a view controlled, appropriately enough, by a view controller.

There is more to this view than just what you see on the screen. There is a specific pattern it follows every time it needs to be seen: the preparation it takes to show up, what happens when it’s visible, and its dismissal and disappearance. It’s all part of an ever-present lifecycle, which we can follow with UIViewController’s lifecycle methods.

There is a distinct difference between UIView and UIViewController. They are separate classes: UIView is a class that represents the screen of the device of everything that is visible to the viewer, while UIViewController is a class that controls an instance of UIView, and handles all of the logic and code behind that view. UIViewController orders which views come onto the screen and when. It’s the director standing in the studio of a live-broadcast television show, telling the technical director when to switch between the different camera feeds to air those different angles. The UIView instances, in this metaphor, are those individual camera feeds.

Joey Smith, Technical Director. TV Studio B Control Room at CSM

Both classes have their own lifecycle: view lifecycle is about the view and its hierarchy of subviews, and how its content is rendered, while the view controller lifecycle is about the controller itself, mostly relevant to how its root view, that UIVIew instance, appears and disappears onto the screen. I will be specifically talking about the view controller’s lifecycle and its corresponding methods, or view event responders.

“When the visibility of its views changes, a view controller automatically calls its own methods so that subclasses can respond to the change.” — UIViewController Class Reference, Apple Developer Tools

The first method to be called is viewDidLoad: This method is called once, as soon as the view in question is called upon. As soon as it loads and is ready to be presented, viewDidLoad: is called automatically by the view controller and populates the view with the code nested within that method.

Anything that needs to be loaded as soon as the view appears should be added to the viewDidLoad: method, i.e. the initial setup your view will take, especially if that form never changes.

viewDidLoad: is called once — and only once — the entire time your app is run. You should never have a reason to call it a second time.

The other methods included in the view controller’s lifecycle are:

  • viewWillAppear:
  • viewDidAppear:
  • viewWillDisappear:
  • viewDidDisappear:

You might have noticed that these methods seem to be paired. Well, they are!

Diagram offered by Apple in their UIViewController Class Reference documentation

The view appears on the screen and becomes visible, and then when it’s dismissed it disappears. The Will methods prepare for the revealing and vanishing acts the view will go through, and the Did methods are triggered when they have completed those actions. They go hand-in-hand, round and round the lifecycle. If a Will method is called, the corresponding Did method needs to be called after, just as when the view appears, it must also disappear.

Sounds familiar? It should.

What a familiar sight!

Views are a lot like the moon: it has a distinct cycle we can actually watch progress. Think of a view on the screen as a full moon. We can see it in front of us on the screen. We also know that it won’t always be there (because it will be replaced by another view, perhaps). Consider the view’s non-visible state as the new moon. Between its appearances, the view is in two other distinct phases. Before it becomes full it has to wax (appearing), and before it becomes new it needs to wane (disappearing). Thinking about views as something cyclical and real makes them a lot easier to comprehend.

Something to note: these are only attached to UIViewController and not to any UIViews themselves. They are called automatically by iOS when the ViewController is triggered. They are simply view-related notifications, responding to view events. We can call them too, to make sure these events are going on without a hitch.

We can also override each of these lifecycle methods. This is useful when we want things to happen at certain times in that view’s life.

viewWillAppear:(BOOL)animated is called when the view controller’s view is about to be added to the view hierarchy, but before any animations have been configured to show the view. Since this method is used to prepare the view to appear onscreen, you can override it for a number of reasons:

  • to populate the view with some light or “inexpensive” data which won’t cause a performance lag simply by adding it
  • to change the status bar to coordinate better with the view that will appear (or, if you prefer, to hide it)
  • to hide fields or disable actions before the view becomes visible

At the time viewWillAppear: is called, the device is ready to show the UIView instance, although the view is not visible quite yet. Keep the functionality light in this method to avoid delays and slowness.

Also, if you need to reload any data because it has changed or to update a table view after the app has loaded (because the amount of sections or rows has changed), you can use the method resetData on self. Remember, viewDidLoad: should never get called after it initially runs. Other methods need to be called in order to achieve the desired effect.

viewDidAppear:(BOOL)animated notifies the view controller that this view was added to the window. You would override this if you want other things to happen related to presenting the view, such as starting an animation or loading data from an API. If you have any processor-heavy (or “expensive”) data, like a massive animation, this is the time to add it. Since the view has appeared by this point, you can add a load bar or waiting screen to make the lag time not only acceptable but expected.

viewWillDisappear:(BOOL)animated is called when the view is about to be removed from the view hierarchy. Override this method if:

  • you have any edits or changes that had been made on this screen and want to save them before the view is wiped clean (views are completely new and fresh every time they appear, and when they disappear they’re gone for good. You can take advantage of this method because it’s called just before the view is removed, so any data saved now will not be deleted)
  • this view was the first responder and you want to trade first responder duties to another view once this one disappears

or anything else that has to do with the view disappearing. It’s along the same vein as viewWillAppear:, only now you’re preparing for the screen to go away.

viewDidDisappear:(BOOL)animated is called when the view has been successfully removed from the view hierarchy. You can override this when you want anything additional to happen in regards to dismissing or hiding the view. This is the clean-up phase, but as most of it is automatically handled, you don’t really need to add too much else.

Each of these methods asks for the BOOL argument animated. If an animation will be used when the view interacts with the view hierarchy, or was used, then animated = YES.

Treat these views like you would any initializer: Always call super on them. These methods will typically look like:

viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];
   // override with everything else it will do here
}

If you don’t call super, the method might display unexpected behavior. No one wants that. You don’t always have to use these methods. You don’t even need to override them if you do. If you ever want to call on them to make sure everything is happening when it’s supposed to happen, add an NSLog() to print out when they run.

Every method except viewDidLoad: can be called more than once during the view controller’s lifecycle, so they should all be coded to be repeatable. If anything has changed and needs to be retained, the information must be saved, because if the changed information is not stored properly and then dismissed, it can cause memory leaks when the view disappears.

Apple doesn’t give much in its reference documentation to these view event responders, but they do offer a note for the appearance of the view:

“If a view controller is presented by a view controller inside of a popover, this method is not invoked on the presenting view controller after the presented controller is dismissed.”

Thanks for making that perfectly clear, Apple!

To gist, the normal viewWillAppear:/viewDidAppear: methods won’t be called when you have a navigation controller’s stack and push a view controller from there. This webpage details a way to ensure those two methods are always called.

Just remember:

  • Never, ever call viewDidLoad: after it’s already been called.
  • These are handy methods, to track and confirm the order of view events (appearance and disappearance).
  • They can be overridden to add functionality based on the phase the view is in (staging visibility and once staging is complete).
  • Always call super on these methods.