UI Kit
01 / 02

Views, Controllers & Auto Layout

UIKit: Views, Controllers & Auto Layout

UIKit is Apple's imperative UI framework for iOS/iPadOS/tvOS — apps are built by directly constructing and manipulating a UIView/UIViewController hierarchy, predating the declarative SwiftUI model.

UIView & UIViewController Basics

class ProfileViewController: UIViewController {

    private let nameLabel = UILabel()
    private let avatarImageView = UIImageView()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Called once, after the view hierarchy is loaded — one-time setup
        view.backgroundColor = .systemBackground
        setupSubviews()
        setupConstraints()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // Called every time this screen is about to become visible
        refreshData()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // Called every time this screen has finished appearing —
        // safe point to start animations tied to visibility
    }

    private func setupSubviews() {
        [nameLabel, avatarImageView].forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview($0)
        }
    }
}

Auto Layout

private func setupConstraints() {
    NSLayoutConstraint.activate([
        avatarImageView.topAnchor.constraint(
            equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 24),
        avatarImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
        avatarImageView.widthAnchor.constraint(equalToConstant: 80),
        avatarImageView.heightAnchor.constraint(equalTo: avatarImageView.widthAnchor), // 1:1 ratio

        nameLabel.topAnchor.constraint(equalTo: avatarImageView.bottomAnchor, constant: 12),
        nameLabel.leadingAnchor.constraint(
            greaterThanOrEqualTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
        nameLabel.trailingAnchor.constraint(
            lessThanOrEqualTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
        nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    ])
}

// UIStackView — arranges a row/column automatically, no per-view constraints
let buttonRow = UIStackView(arrangedSubviews: [cancelButton, saveButton])
buttonRow.axis = .horizontal
buttonRow.distribution = .fillEqually
buttonRow.spacing = 12

// Animating a constraint change — layoutIfNeeded() forces the pending
// layout pass to happen inside the animation block so it interpolates
heightConstraint.constant = 200
UIView.animate(withDuration: 0.3) {
    self.view.layoutIfNeeded()
}

Frame vs Bounds & Trait Changes

  • frame: a view's position and size in its superview's coordinate system.

  • bounds: a view's size in its own internal coordinate system — origin shifts as UIScrollView content scrolls, while frame stays fixed.

  • intrinsicContentSize: a view's natural content-derived size (UILabel sized to its text, UIButton to its title) — lets Auto Layout skip explicit width/height for these.

  • safeAreaLayoutGuide: constrains content to stay clear of the notch/Dynamic Island, status bar, and home indicator.

  • traitCollectionDidChange(_:) reacts to light/dark mode, size class, or Dynamic Type changes at runtime.

Gestures & Target-Action

// Target-action — the standard UIKit event-handling pattern
saveButton.addTarget(self, action: #selector(didTapSave), for: .touchUpInside)

@objc private func didTapSave() {
    // ...
}

// Gesture recognizers wrap raw touch tracking into reusable detectors
let tap = UITapGestureRecognizer(target: self, action: #selector(didTapAvatar))
avatarImageView.isUserInteractionEnabled = true // image views don't accept touches by default
avatarImageView.addGestureRecognizer(tap)

@objc private func didTapAvatar() {
    presentImagePicker()
}

// Retain-cycle-safe closure capture — the same ARC pattern as elsewhere in Cocoa
completionHandler = { [weak self] result in
    guard let self else { return }
    self.handle(result)
}

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free