SwiftUI ships the common controls — text, images, buttons, text fields, toggles, pickers — that look native out of the box. Assemble them, and use Form for settings-style grouped input.
SwiftUI provides ready-made, native-looking building blocks: Text for labels, Image (including SF Symbols), Button for actions, and simple containers. You style them with modifiers rather than configuring raw widgets, getting correct appearance, dark mode, and accessibility for free. Knowing the handful you use daily covers most UI.
struct ContactCard: View {
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Image(systemName: "person.circle.fill") // an SF Symbol
.font(.largeTitle).foregroundStyle(.blue)
Text("Ada Lovelace").font(.headline)
Text("ada@example.com").font(.subheadline).foregroundStyle(.secondary)
Button("Message") { /* action */ }
.buttonStyle(.borderedProminent)
}
.padding()
}
}
// Text, Image (+ SF Symbols), Button — native + accessible out of the box.Interactive controls are bindings-driven: TextField, Toggle, Slider, Stepper, and Picker each take a binding to the state they edit, following the state model from the previous lesson. You configure keyboard type, secure entry for passwords, and options for pickers. This consistent bindings pattern makes input state explicit and easy to reason about.
struct FormControls: View {
@State private var name = ""
@State private var password = ""
@State private var notify = true
@State private var plan = "Free"
var body: some View {
VStack {
TextField("Name", text: $name)
SecureField("Password", text: $password) // masked input
Toggle("Notifications", isOn: $notify)
Picker("Plan", selection: $plan) {
Text("Free").tag("Free")
Text("Pro").tag("Pro")
}
}
}
}
// every control edits a BINDING to your state — the value model from lesson 3.Form lays out controls in the grouped, sectioned style of the iOS Settings app, handling styling and spacing for you. Wrap related controls in Section with headers to build settings screens and data-entry forms that look native with almost no styling code. It is the idiomatic container for any settings-like screen.
struct SettingsForm: View {
@State private var notify = true
@State private var volume = 0.5
var body: some View {
Form {
Section("Notifications") {
Toggle("Push alerts", isOn: $notify)
}
Section("Audio") {
Slider(value: $volume)
Stepper("Volume steps", value: $volume, in: 0...1, step: 0.1)
}
}
}
}
// Form gives the grouped, native "Settings app" look with zero styling code.Transient UI — alerts, confirmation dialogs, and modal sheets — is presented declaratively from state: a boolean binding controls whether the alert or sheet shows. Rather than imperatively "presenting" something, you attach an alert or sheet modifier bound to state. This fits SwiftUI’s model and keeps popups predictable.
struct DeleteButton: View {
@State private var showConfirm = false
var body: some View {
Button("Delete", role: .destructive) { showConfirm = true }
.alert("Delete item?", isPresented: $showConfirm) { // bound to state
Button("Delete", role: .destructive) { /* delete */ }
Button("Cancel", role: .cancel) { }
}
}
}
// declarative presentation: a boolean state decides whether the alert/sheet
// shows. .sheet(isPresented:) works the same way for modal screens.