Posted in

Explained viewController lifecycle – Swift

viewcontroller lifecycle

Lets go through the View Controller lifecycle in Swift, yes it is definitely different in SwiftUI.

1. loadView()

In iOS UI can be generated in 3 ways, using storyboard, using xib or using code only. If we are creating UI using code, loadView() is responsible for creating and assigning the main view of your view controller.
You override loadView() when you want to create the entire view hierarchy manually in code, without using a storyboard or .xib.

override func loadView() {
  let customView = UIView()
  customView.backgroundColor = .white
  let label = UILabel()
  label.text = "Hello, Swift!"
  label.textAlignment = .center
  label.frame = CGRect(x: 50, y: 100, width: 200, height: 40)
  customView.addSubview(label)

  self.view = customView // Must assign a view to self.view    
}

⚠️ Important: If you override loadView(), you must assign something to self.view. If not, the app will crash.

2. viewDidLoad()

This method is called once in view controller’s life cycle, after the view controller’s view is loaded into memory, but before it appears on screen. It’s where you do one-time setup work like initializing values, setting up UI, and configuring the view.

override func viewDidLoad() {
    super.viewDidLoad()

    // Set background color
    view.backgroundColor = .white

    // Configure label
    welcomeLabel.text = "Welcome to the app!"
    welcomeLabel.textColor = .darkGray

    // Set delegate for table view
    tableView.delegate = self
    tableView.dataSource = self

    // Register custom cell
    tableView.register(UINib(nibName: "MyCustomCell", bundle: nil), forCellReuseIdentifier: "cell")
}

3. viewWillAppear()

This method is called every time the view is about to appear, even after coming back from another screen. It’s ideal for tasks that need to refresh each time the view becomes visible.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // Update welcome label with latest username
    welcomeLabel.text = "Welcome, \(UserDefaults.standard.string(forKey: "username") ?? "Guest")"

    // Refresh table data
    tableView.reloadData()

    // Show navigation bar if previously hidden
    navigationController?.setNavigationBarHidden(false, animated: true)
}

4. viewWillLayoutSubviews()

This method is a lifecycle method in UIViewController that is called just before the layout engine lays out your subviews.

In short: It’s triggered before the system calculates frames and sizes of subviews.

When Is It Called?

  • Every time the view’s bounds change (like after rotation or resizing)
  • After viewDidLoad() and before viewDidAppear()
  • Whenever setNeedsLayout() or layoutIfNeeded() is called
override func viewWillLayoutSubviews() {
    super.viewWillLayoutSubviews()

    // Adjust a view's frame dynamically
    myButton.frame = CGRect(
        x: 20,
        y: view.bounds.height - 100,
        width: view.bounds.width - 40,
        height: 50
    )
}

This is helpful when your layout depends on actual view size, which isn’t finalized yet in viewDidLoad.

5. viewDidLayoutSubviews()

This method is a UIKit UIViewController method called after the system finishes laying out the view controller’s subviews.

In simple words: It’s your chance to react to the final layout of the views — their sizes and positions have been set. Once layout is done, you can animate based on final positions.

6. viewDidAppear()

This method is a UIViewController lifecycle method that is called after the view has fully appeared on screen and is visible to the user.

It’s the first place in the lifecycle where you can safely interact with things that need the view to be 100% visible. Here you can start animations, sending analytics, beginning of timers, etc.

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // Send analytics event
    Analytics.logEvent("home_screen_viewed", parameters: nil)

    // Start a loading animation
    loadingSpinner.startAnimating()

    // Show welcome alert
    if isFirstLaunch {
        showWelcomeAlert()
    }
}

7. viewWillDisappear()

This method is called just before the view controller’s view is removed from the screen — either because:

  • You’re navigating away (e.g., pushing a new view controller)
  • The view is being dismissed

Think of it as your “clean up or pause” moment before the view leaves. Here you can pause timers, remove observers, stop loading spinners, etc.

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    // Stop the timer
    refreshTimer.invalidate()

    // Pause video playback
    videoPlayer.pause()

    // Save draft data
    saveFormState()

    // Log analytics
    Analytics.logEvent("profile_view_closed", parameters: nil)
}

8. viewDidDisappear()

is a UIKit UIViewController lifecycle method that’s called after the view has fully disappeared from the screen.

It’s the moment when the view is completely gone, and you can safely release or stop things that you no longer need. Here you can cancel network calls, stop timers, or animations, clean up resources, etc.

override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)

    // Cancel any webservice tasks
    loadingTask?.cancel()

    // Stop the music if it's playing
    musicPlayer.stop()

    // Save progress
    saveUserProgress()

    // Log for analytics
    Analytics.logEvent("checkout_screen_closed", parameters: nil)
}

9. deinit()

deinit is a deinitializer — a special method in Swift that’s called just before an instance of a class is deallocated from memory.

Think of it as your “cleanup before death” function — used to release resources, remove observers, or close files.

When Does deinit Get Called?

  • Automatically by the system when the reference count of a class instance reaches zero
  • It’s only available in classes (not structs or enums)
  • You never call deinit directly — Swift does it for you
  • If deinit isn’t being called, it’s likely because: something is strongly retaining the object (e.g., a closure, delegate, or view hierarchy)

10. didReceiveMemoryWarning()

This method in UIViewController that gets called when the system is running low on memory.

It’s your chance to free up memory by releasing anything that can be recreated later (like cached images or data).

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()

    // Clear in-memory image cache
    imageCache.removeAllObjects()

    // Nullify large data that can be fetched again
    largeJSONData = nil

    print("Memory warning received. Cleaned up resources.")
}

11. viewWillTransition(to:with:)

This method is called whenever the view controller’s size is about to change, such as:

  • Device rotation (portrait ↔ landscape)
  • Multitasking size changes on iPad
  • Any layout changes that affect view.bounds.size

It gives you a chance to adjust layout or animations before the size actually changes.

When to Use:

  • When you want custom animations during rotation
  • When manual layout is being used
  • When you want to log orientation or size changes cleanly

Conclusion: Understanding UIKit Lifecycles

1. View Lifecycle (Manages View Appearance)

MethodPurpose
viewDidLoad()One-time setup when view loads
viewWillAppear()Prepare UI just before view appears
viewDidAppear()Start animations, tracking after visible
viewWillDisappear()Pause tasks before view disappears
viewDidDisappear()Final cleanup after view is hidden

2. Layout Lifecycle (Handles Size & Orientation Changes)

MethodPurpose
viewWillLayoutSubviews()Adjust layout before subviews are laid out
viewDidLayoutSubviews()Make changes after layout is complete
viewWillTransition(to:with:)Handle size/rotation changes (e.g., rotate)

3. Memory Lifecycle (Manages Resources)

MethodPurpose
didReceiveMemoryWarning()Free up memory during system pressure
deinitFinal cleanup when object is deallocated

Thank you!

I'm a passionate iOS Developer with over 10 years of experience building high-quality iOS apps using Objective-C, Swift, and SwiftUI. I created iostutor.com to share practical tips, tutorials, and insights for developers of all levels.

When I’m not coding, I enjoy exploring new technologies and writing content — from technical guides to stories and poems — with the hope that it might help or inspire someone, somewhere.

Leave a Reply

Your email address will not be published. Required fields are marked *