ViewController Lifecycle in iOS: Key Concepts, Stages, and Methods

Author: IT Sectr Published: 2026-03-05 Reading time: 9 min

ViewController Lifecycle is the sequence of methods that UIKit automatically calls when managing screens in iOS. According to Apple Documentation, each UIViewController goes through a predictable set of states: from View creation to its appearance and disappearance. Understanding the order and purpose of these methods is a necessary condition for stable iOS app operation.

Key Takeaways

  • ViewController Lifecycle consists of six UIViewController methods called by UIKit in a strict order
  • loadView creates the View hierarchy if you are not using Storyboard
  • viewDidLoad is called once and is suitable for initial screen setup
  • viewWillAppear and viewDidAppear fire on every appearance
  • viewWillDisappear and viewDidDisappear — for saving state and cleanup

What is ViewController Lifecycle

ViewController Lifecycle is a set of methods that UIViewController receives from UIKit throughout its existence. Each screen in an iOS app sequentially goes through the stages of creation, View loading, appearing on screen, disappearing, and memory deallocation. UIKit automatically calls the corresponding methods at each stage, and the developer overrides them to add custom logic.

The UIViewController architecture is fundamental to UIKit and remains relevant even in the SwiftUI era — many projects still use the classic approach or a hybrid architecture. Understanding the Lifecycle allows you to predict when subviews are available, when it is safe to modify the layout, and which operations to perform when the screen appears or disappears.

Each lifecycle method has a specific purpose: some are called once during the entire controller’s lifetime, others — on every appearance or disappearance. Mixing logic between methods leads to hard-to-find bugs: memory leaks, incorrect data updates, and unnecessary network requests.

Full Cycle of UIViewController Methods

Six methods form the complete UIViewController lifecycle. The call order is fixed and does not depend on the navigation method — push, present, or unwind segue all follow the same schedule.

loadView — Creating the Root View

loadView is the first method of the cycle, called when the controller’s View does not yet exist. If you use Storyboard, UIKit automatically loads the View from the xib file. When creating the interface programmatically, you override this method by assigning the root View manually. In most projects, loadView is left untouched — work is done in viewDidLoad.

Overriding loadView is only needed in specific cases: when the entire interface is created in code without Storyboard, or when the root View needs to be of a non-standard class. Apple recommends not calling super.loadView when overriding — you take full responsibility for creating the View.

swift
override func loadView() {
    view = UIView()
    view.backgroundColor = .white
}

viewDidLoad — One-Time Initialization

viewDidLoad is the most commonly used method of the cycle. It is called once after the View is loaded into memory but is not yet displayed on screen. Here you configure subviews, populate tables with data, register cells, and subscribe to notifications that last the entire lifetime of the controller.

An important feature: viewDidLoad is not called again when the screen is shown again. If you need to update data every time the screen appears — use viewWillAppear. Place only one-time operations in viewDidLoad that are required for basic configuration.

viewWillAppear — Preparation Before Display

viewWillAppear is called each time right before the View becomes visible to the user. This method receives an animated parameter indicating whether the appearance is animated. Here you update data, reload tables, configure the NavigationBar, and hide or show elements depending on the application state.

Use viewWillAppear for state synchronization between screens: if the user could have changed data on the previous screen, this method is the right place to update the interface. Each call to viewWillAppear precedes the screen appearance, even when returning from a child controller.

viewDidAppear — Screen Fully Visible

viewDidAppear notifies that the View has fully appeared on screen and all transition animations are complete. At this point, the screen is ready for interaction — the user sees the full interface and can interact with it. This method is suitable for starting animations that should begin after appearance, starting timers, and tracking analytics impressions.

Unlike viewWillAppear, viewDidAppear guarantees that the screen is not only visible but also fully rendered. If you start an animation in viewWillAppear, some frames may be skipped because UIKit has not yet completed the transition. For smooth animations, use viewDidAppear.

viewWillDisappear — Preparation for Hiding

viewWillDisappear is called before the View disappears from screen — when transitioning to another controller, closing a modal window, or suspending the app. This is the right place to save state, unsubscribe from notifications, stop active processes, and release resources that are not needed when the screen is not visible.

It is important to remember: viewWillDisappear does not guarantee that the View will ultimately disappear — the gesture may be cancelled. Therefore, save critical data also in viewDidDisappear, which is called only after the actual disappearance.

viewDidDisappear — Screen Is Hidden

viewDidDisappear completes the appearance and disappearance cycle. It is called after the View is already hidden from the screen. In this method, animations are finally stopped, temporary objects are removed, and data saving initiated in viewWillDisappear is confirmed.

This method also precedes the controller’s deinit — if your UIViewController is being destroyed, viewDidDisappear will be the last Lifecycle method before deinit is called. Use it for final cleanup that should happen before the object is destroyed.

When Each Method Is Called

The sequence of calls depends on how the screen appears: for the first time, when going back, or when presented modally. Let’s consider three main scenarios from UIKit’s perspective.

Order on First Opening

When a screen appears for the first time, UIKit goes through the full creation cycle: loadView is called, then viewDidLoad, after which the appearance animation starts. During the animation, viewWillAppear is called, and after completion — viewDidAppear. This is the only scenario where all methods from loadView to viewDidAppear fire sequentially.

swift
override func viewDidLoad() {
    super.viewDidLoad()
    print("viewDidLoad — View loaded into memory")
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    print("viewWillAppear — About to appear")
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    print("viewDidAppear — Screen fully visible")
}

Order When Going Back

When the user goes back to a previous screen, UIKit does not call viewDidLoad again — the View is already loaded into memory. Instead, only viewWillAppear and viewDidAppear are called on the returning screen, and on the current one — viewWillDisappear and viewDidDisappear. loadView and viewDidLoad are skipped since the screen already exists in the navigation stack.

Special Cases with Present and Dismiss

Modal presentation follows the same rules: the new controller goes through the full cycle on first appearance, while the current one receives viewWillDisappear and viewDidDisappear. On dismiss, the order is reversed: the returning controller gets viewWillAppear and viewDidAppear again, while the dismissed one gets the final methods. This behavior is uniform for all transition types in UIKit.

Practical Usage Scenarios

Let’s look at four key scenarios where understanding the Lifecycle directly impacts code quality and user experience. For each scenario, we provide an example with recommendations.

Data Initialization in viewDidLoad

viewDidLoad is the place for initial setup that does not depend on screen visibility. Here you configure the collectionView, register nib files for cells, create data sources and layouts. If you are loading data from the network, in viewDidLoad it is better to only initiate the request, and update the UI in viewWillAppear when the screen is ready for display.

swift
override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(
        MyCell.self,
        forCellReuseIdentifier: MyCell.identifier
    )
    viewModel.loadInitialData()
}

Content Update in viewWillAppear

Use viewWillAppear for data synchronization every time the screen appears. For example, if the user could have changed settings on the previous screen, here you update the displayed values, reload the table, and adjust the NavigationBar state. This guarantees that the screen always shows up-to-date data in any navigation scenario.

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    tableView.reloadData()
    navigationController?.setNavigationBarHidden(false, animated: animated)
}

Analytics and Animations in viewDidAppear

viewDidAppear is ideal for starting animations that should begin after the user has seen the screen. Here you also send analytics events: screen view, onboarding start, or video playback initiation. Starting animations before the transition is complete leads to janky interface — UIKit does not have enough time to prepare a sufficient number of frames.

State Saving in viewWillDisappear

In viewWillDisappear, you save drafts, stop timers, and unsubscribe from NotificationCenter. This is the last moment when the screen is still visible and accessible for operations requiring user context. For critical data, additionally use viewDidDisappear as a safeguard against cancelled gestures.

Common Mistakes When Working with Lifecycle

Incorrect usage of lifecycle methods is one of the most frequent sources of bugs in iOS apps. Let’s look at the main mistakes developers make at different stages of working with UIViewController.

The first mistake — creating subviews in init or loadView when using Storyboard. If you are using Interface Builder, do not override loadView unnecessarily. Creating a View in loadView when a storyboard exists results in ignoring the xib file and an empty screen.

The second mistake — subscribing to keyboard notifications in viewDidLoad without unsubscribing. If you subscribed to UIResponder.keyboardWillShowNotification but did not unsubscribe when the screen is hidden, the block will be called even after the controller’s deinit — this is a memory leak with potential app crashes.

The third mistake — timers and network requests started before the screen appears. Loading images or performing animations when the View is not yet visible is a waste of resources. Move visual updates to viewWillAppear or viewDidAppear.

The fourth mistake — saving data only in viewWillDisappear. With an interactive pop gesture, the user may start a swipe and cancel it — the method was called but the screen did not disappear. Duplicate critical saving in viewDidDisappear or in the applicationDidEnterBackground handler.

Frequently Asked Questions

How many times is viewDidLoad called during a controller’s lifetime?

Once — after the View is loaded into memory. When the screen appears again, viewDidLoad is not called. If you need to recreate the View, the controller must be destroyed and created anew.

What happens if you don’t call super in viewDidLoad?

UIKit requires calling super.viewDidLoad for the lifecycle to work correctly. Without it, issues with layout updates and transition handling may occur. Always call super first thing inside the method.

Can I use Storyboard and programmatic loadView at the same time?

Not recommended. If the controller is initialized from Storyboard, UIKit automatically loads the View from the xib. Overriding loadView cancels this process, and your storyboard will be ignored.

How to properly unsubscribe from NotificationCenter?

Subscribe in viewDidLoad or viewWillAppear, and unsubscribe in viewWillDisappear or viewDidDisappear, using a weak reference to self to avoid memory leaks with closures.

Why is viewDidDisappear not called on force quit?

Force quit kills the process abruptly — UIKit does not have time to call Lifecycle methods. To save data, use the UIApplication.willTerminateNotification in AppDelegate.

Summary

  • ViewController Lifecycle consists of six methods called by UIKit in a fixed order
  • loadView and viewDidLoad fire once when the controller is created
  • viewWillAppear and viewDidAppear are called on every screen appearance
  • viewWillDisappear and viewDidDisappear — on every disappearance
  • Each method has a specific purpose — mixing logic leads to bugs
  • Notification subscriptions should always be balanced with unsubscription in the corresponding method
  • Use viewDidAppear for animations and analytics, and viewWillDisappear for saving state

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also