Looking to understand the iOS UIViewController lifecycle
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
UIViewController lifecycle methods tell you when a controller is created, when its view is loaded, when that view becomes visible, and when it leaves the screen. Knowing those boundaries is how you decide where setup, refresh, animation, and cleanup code should live.
Object Creation Versus View Loading
A view controller object can exist before its view hierarchy has been loaded. That is why setup work is split across several methods instead of happening in one place.
The most common early lifecycle points are:
- '
initfor dependency injection and object setup' - '
loadViewwhen building the view hierarchy manually' - '
viewDidLoadafter the view hierarchy is in memory'
Most controllers override viewDidLoad, not loadView, because UIKit can usually create the root view for you.
viewDidLoad is a good home for one-time setup such as building subviews, registering table or collection view cells, assigning delegates, and wiring view model bindings. It is usually the wrong place for work that must happen every time the screen reappears.
Appearance and Disappearance Callbacks
When a controller moves on or off screen, UIKit calls appearance methods in a predictable order. These methods matter because some work depends on visibility, not just existence.
Use viewWillAppear for refreshing labels, toggles, or derived UI state that may have changed while the screen was hidden. Use viewDidAppear for work that requires the view to be visible, such as starting a tutorial animation or logging a screen view event. The disappearance callbacks are the right place to stop temporary behavior like timers, editing sessions, or observation tied only to visibility.
Layout Callbacks Are Different
Many lifecycle bugs come from confusing loading with layout. viewDidLoad means the view exists in memory. It does not guarantee final sizes. If you need the actual bounds of subviews, use layout callbacks such as viewDidLayoutSubviews.
That distinction matters when you set corner radii based on width, create gradient layers, or position custom drawing. Code that depends on geometry often behaves incorrectly if it runs too early.
A Practical Mental Model
The simplest way to reason about the lifecycle is:
- configure the controller in
init - do one-time view setup in
viewDidLoad - refresh screen state in
viewWillAppear - start visible-only effects in
viewDidAppear - stop them in
viewWillDisappearorviewDidDisappear
Once you follow that split consistently, controllers become easier to test and easier to maintain. It also reduces the temptation to throw unrelated code into viewDidLoad just because that method is familiar.
Common Pitfalls
- Putting every update in
viewDidLoadeven though it often runs only once for a loaded view. - Reading final view sizes before layout has happened.
- Starting animations or analytics before the controller is actually visible.
- Forgetting to call
superin overridden lifecycle methods. - Treating lifecycle callbacks as a substitute for proper state management.
Summary
- The lifecycle describes loading, layout, appearance, and disappearance of a view controller.
- '
viewDidLoadis for one-time setup after the view hierarchy exists.' - '
viewWillAppearandviewDidAppearare for work tied to becoming visible.' - Layout-dependent code belongs in layout callbacks, not just loading callbacks.
- A clear separation of responsibilities makes controller code smaller and less fragile.

