UIKit View Controllers — Comprehensive Guide

Mastering UIViewController & Advanced Patterns

1  Anatomy & Responsibilities

UIViewController owns a root view and orchestrates:

2  Lifecycle Deep-Dive

2.1 Typical Sequence

  1. init(coder:) / init(nibName:bundle:)
  2. loadView()
  3. viewDidLoad()
  4. viewWillAppear(_ animated: Bool)
  5. viewWillLayoutSubviews()
  6. viewDidLayoutSubviews()
  7. viewDidAppear(_ animated: Bool)
  8. viewWillDisappear(_ animated: Bool)
  9. viewDidDisappear(_ animated: Bool)
  10. deinit

2.2 Lifecycle Example

class GreetingViewController: UIViewController {
    override func loadView() {
        let root = UIView()
        root.backgroundColor = .systemBackground

        let label = UILabel()
        label.text = "Hello, UIKit!"
        label.font = .systemFont(ofSize: 32, weight: .bold)
        label.translatesAutoresizingMaskIntoConstraints = false
        root.addSubview(label)
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: root.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: root.centerYAnchor)
        ])
        self.view = root
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        print("View finished loading")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("Will appear, animated =", animated)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("Did appear, animated =", animated)
    }
}

3  Key Methods & Parameter Explanations

3.1 Loading & Memory

  • loadView() — create/assign root view; must set self.view.
  • viewDidLoad() — one-time setup detached from geometry (e.g. data bindings).
  • didReceiveMemoryWarning() (deprecated iOS 13) — purge caches/non-visible subviews.

3.2 Appearance Callbacks

  • viewWillAppear(_ animated: Bool) — refresh UI; animated signals transition style.
  • viewDidAppear(_ animated: Bool) — start heavy animations, analytics.
  • viewWillDisappear/DidDisappear — pause timers, save edits, stop AV playback.

3.3 Layout

  • viewWillLayoutSubviews() — tweak constraints before AutoLayout finalizes.
  • viewDidLayoutSubviews() — update scrollView contentSize, gradient layers, etc.

3.4 Presentation & Navigation

present(_ viewControllerToPresent: UIViewController,
        animated: Bool,
        completion: (() -> Void)? = nil)

viewControllerToPresent — modal target.
animatedtrue for animated transition.
completion — runs after animation ends.

  • dismiss(animated:completion:)
  • navigationController?.pushViewController(_:animated:)
  • performSegue(withIdentifier:sender:) & prepare(for:sender:)

3.5 Child View Controllers

  • addChild(_ childController: UIViewController)
  • willMove(toParent:) / didMove(toParent:)
  • removeFromParent()

4  Practical Examples

4.1 Modal Form Sheet

let editorVC = TextEditorViewController()
editorVC.modalPresentationStyle = .formSheet
present(editorVC, animated: true)

4.2 Passing Data via Segue

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "showDetail",
       let detail = segue.destination as? DetailViewController,
       let note   = sender as? Note {
        detail.note = note
    }
}

4.3 Custom Container Controller

class SplitContainerController: UIViewController {
    private let master = MasterViewController()
    private let detail = DetailViewController()

    override func viewDidLoad() {
        super.viewDidLoad()
        add(master, constrainedTo: view.leadingAnchor)
        add(detail, constrainedTo: view.trailingAnchor)
    }

    private func add(_ child: UIViewController, constrainedTo anchor: NSLayoutXAxisAnchor) {
        addChild(child)
        child.view.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(child.view)
        NSLayoutConstraint.activate([
            child.view.topAnchor.constraint(equalTo: view.topAnchor),
            child.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            child.view.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5),
            child.view.leadingAnchor.constraint(equalTo: anchor)
        ])
        child.didMove(toParent: self)
    }
}

5  Best Practices & Common Pitfalls

  • Defer network calls until viewWillAppear(_:) if data may change while hidden.
  • Avoid mutating constraints in viewDidLayoutSubviews() if it triggers another layout pass.
  • Detach observers in deinit or viewWillDisappear(_:) to prevent leaks.
  • Favor container composition over massive subclasses.

6  Quick Reference Cheat Sheet

  • Lifecycle: loadViewviewDidLoadviewWillAppearviewDidAppearviewWillDisappearviewDidDisappear
  • Presentation: present(_:animated:completion:)
  • Navigation: UINavigationController, UITabBarController
  • Segues: performSegue, prepare(for:sender:)
  • Containment: addChild, didMove, removeFromParent
  • Rotation: override viewWillTransition(to:with:)
  • Status Bar: override preferredStatusBarStyle

7  Further Reading

• Apple Developer Documentation → UIViewController
Advanced iOS App Architecture (Objc.io)
• WWDC Sessions: “View Controller Advanced Techniques”, “State Restoration in Depth”.