← All courses

UIKit: View Controllers & Lifecycle

🗓 May 31, 2026 ⏱ 2 min read

What is a view controller?

In UIKit, a screen is managed by a UIViewController. It owns a tree of views (the visual elements) and contains the logic for that screen. Think of it as the “brain” of one screen: it sets up the UI, responds to user actions, and reacts as the screen appears and disappears.

A basic view controller

class ProfileViewController: UIViewController {

    private let nameLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        nameLabel.text = "Anand Gaur"
        nameLabel.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(nameLabel)
        NSLayoutConstraint.activate([
            nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            nameLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20)
        ])
    }
}

The view controller lifecycle

UIKit calls a series of methods as your screen comes and goes. Knowing the order tells you exactly where to put each kind of code:

  • viewDidLoad() — called once, when the view is first loaded into memory. Do one-time setup here (add subviews, configure layout).
  • viewWillAppear(_:) — just before the screen becomes visible. Refresh data that might have changed.
  • viewDidAppear(_:) — after the screen is fully visible. Start animations or analytics.
  • viewWillDisappear(_:) — the screen is about to leave. Save state, pause work.
  • viewDidDisappear(_:) — the screen has gone.
viewDidLoad viewWillAppear viewDidAppear viewWillDisappear…

Why viewDidLoad vs viewWillAppear matters

viewDidLoad runs only once, so building the UI there is efficient. But it does not run again when you return to the screen. If you need fresh data each time the screen appears (say, after editing a profile), put that refresh in viewWillAppear — otherwise you’ll show stale data.

Responding to actions

let button = UIButton(type: .system)
button.setTitle("Tap", for: .normal)
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)

@objc func didTap() {
    print("Button tapped")
}

Common mistakes

  • Refreshing data in viewDidLoad and wondering why it’s stale on return (use viewWillAppear).
  • Forgetting translatesAutoresizingMaskIntoConstraints = false when using Auto Layout in code.
  • Doing heavy work in viewDidLoad that delays the screen appearing.
Summary: A UIViewController manages one screen. Do one-time setup in viewDidLoad, and per-appearance refreshes in viewWillAppear. Learn the lifecycle order — it tells you where each piece of code belongs.