Activity 1
Build an empty-state view
Create a reusable view containing an image, title, message, and action button with one configure method.
Hint: Expose the action without exposing every internal control.
UIKit Development
Construct readable UIKit view hierarchies in code with clear ownership, configuration, and reusable components.
Before you begin
Learning stage
Programmatic UIKit makes the hierarchy, configuration, and constraints visible in source control. A controller can override loadView, create one root view, add its descendants, and assign the root to view. Every controller instance needs its own view objects; views should not be shared between controllers.
Use private lazy or immediately initialized properties for important controls, and factory helpers for repeated styling. Set translatesAutoresizingMaskIntoConstraints to false before applying Auto Layout. Organize setup into small methods when it clarifies hierarchy, configuration, constraints, and actions rather than scattering mutations across the lifecycle.
A reusable visual unit usually belongs in a UIView subclass with an explicit configure method or immutable inputs. A screen controller should coordinate state and navigation, not contain every styling detail. Keep models independent of UIKit so interface choices can change without rewriting domain behavior.
Learning stage
A small reusable card keeps presentation details out of the screen controller.
import UIKit
final class CourseCardView: UIView {
private let titleLabel = UILabel()
private let progressView = UIProgressView(progressViewStyle: .default)
private lazy var stack = UIStackView(arrangedSubviews: [titleLabel, progressView])
override init(frame: CGRect) {
super.init(frame: frame)
directionalLayoutMargins = .init(top: 20, leading: 20, bottom: 20, trailing: 20)
backgroundColor = .secondarySystemBackground
layer.cornerRadius = 16
stack.axis = .vertical
stack.spacing = 12
stack.translatesAutoresizingMaskIntoConstraints = false
addSubview(stack)
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
stack.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
stack.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
])
}
required init?(coder: NSCoder) { fatalError("Use init(frame:)") }
func configure(title: String, progress: Float) {
titleLabel.text = title
progressView.progress = progress
}
}Learning stage
Connect the UIKit concept to complete, maintainable application features.
Learning stage
Identify lifecycle, ownership, identity, layout, accessibility, and user-experience risks.
Learning stage
Build the behavior in a small Xcode project and inspect it across realistic states.
Activity 1
Create a reusable view containing an image, title, message, and action button with one configure method.
Hint: Expose the action without exposing every internal control.
Activity 2
Build a controller with a heading, course card, and bottom action constrained through layout guides.
Hint: Sketch the hierarchy before writing constraints.
Learning stage
Answer each question before revealing the explanation.
QUESTION 1
Creating and assigning the root view hierarchy
The controller loads its hierarchy lazily and loadView is the dedicated construction point.
QUESTION 2
A small semantic configuration and interaction API rather than all internal subviews.
Encapsulation lets the implementation change without affecting every caller.
Learning stage
Explain the design in terms of lifecycle, identity, ownership, system behavior, and trade-offs.
Short answer: Create the hierarchy in loadView, configure once, constrain with layout guides, and extract focused reusable views.
I keep controls private, make ownership explicit, separate hierarchy/configuration/constraints/actions when helpful, and give reusable views semantic APIs. The controller coordinates state and navigation while components own presentation details.
Possible follow-ups
Learning stage
Programmatic UIKit is maintainable when hierarchy and ownership are explicit, controls stay encapsulated, and focused views provide semantic configuration APIs.