Forms & User Input

Reviewed & published by Brayan K

Apps live on user input. SwiftUI's Form gives you a polished, settings-style container, and controls like TextField, Toggle, Picker, and Stepper read and write your state through bindings. Wire them up and your screen becomes interactive.

Part of the free Swift course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

Building iOS Apps with SwiftUI • Intermediate

What You'll Learn in This Lesson

1️⃣ Form & Section

Form arranges controls into a scrollable, settings-style layout. Group related controls with Section, optionally adding a header. SwiftUI handles all the styling.

import SwiftUI

// Form groups input controls into a styled, scrollable layout — perfect for
// settings and sign-up screens. Use Section to group related controls.
struct SettingsForm: View {
    @State private var username = ""
    @State private var notifications = true

    var body: some View {
        Form {
            Section("Account") {
                TextField("Username", text: $username)
            }
            Section("Preferences") {
                Toggle("Notifications", isOn: $notifications)
            }
        }
    }
}

2️⃣ TextField — Capturing Text

TextField binds to a String via the $ prefix, so typing updates your state live. Modifiers like .keyboardType tune the keyboard, and SecureField hides sensitive input.

import SwiftUI

// TextField binds to a String. The $ prefix passes a two-way Binding so what
// the user types flows into your @State, and vice versa.
struct LoginFields: View {
    @State private var email = ""
    @State private var password = ""

    var body: some View {
        Form {
            TextField("Email", text: $email)
                .keyboardType(.emailAddress)        // email-friendly keyboard
                .textInputAutocapitalization(.never)
            SecureField("Password", text: $password) // hides characters

            // Live preview of the bound state:
            Text("Typing: \(email)")
        }
    }
}

3️⃣ Toggle, Picker & Stepper

Toggle binds to a Bool, Picker to a selection (matched by each option's tag), and Stepper to a number it can increment within a range. All read and write through bindings.

import SwiftUI

// Toggle binds to a Bool, Picker to a selection, Stepper to a number.
struct OrderForm: View {
    @State private var isGift = false
    @State private var size = "Medium"
    @State private var quantity = 1

    let sizes = ["Small", "Medium", "Large"]

    var body: some View {
        Form {
            Toggle("Wrap as gift", isOn: $isGift)

            Picker("Size", selection: $size) {
                ForEach(sizes, id: \.self) { s in
                    Text(s).tag(s)        // tag matches the selection value
                }
            }

            Stepper("Quantity: \(quantity)", value: $quantity, in: 1...10)
        }
    }
}

Your turn. Fill in the blanks to wire up a small sign-up form.

// 🎯 YOUR TURN — fill in each ___ then preview in Xcode.
import SwiftUI

struct SignUpForm: View {
    @State private var name = ""
    @State private var agreed = false
    @State private var age = 18

    var body: some View {
        Form {
            // 1) A text field bound to 'name' (pass it as a binding)
            TextField("Your name", text: ___name)   // 👉 the binding prefix

            // 2) A toggle bound to 'agreed'
            ___("I agree to the terms", isOn: $agreed) // 👉 the on/off control

            // 3) A stepper that changes 'age' between 13 and 120
            Stepper("Age: \(age)", value: $age, in: 13...120)
        }
    }
}

Pro Tips

Common Errors (and the fix)

Mini-Challenge: Booking Form

Build a Form with two sections. In "Guest" put a TextField for the name. In "Details" put a Picker to choose a room type from three options, a Stepper for the number of nights (1 to 14), and a Toggle for "Include breakfast". Show a live summary Text below that reflects all the bound state.

🎉 Lesson Complete!

Practice quiz

What is the Form view used for?

  • Drawing charts
  • Grouping controls for data entry in a styled, scrollable container
  • Networking
  • Playing audio

Answer: Grouping controls for data entry in a styled, scrollable container. Form groups input controls into a platform-styled, scrollable settings-like layout.

What does TextField bind to?

  • A Bool
  • A String (the editable text) via a binding
  • An Int only
  • Nothing

Answer: A String (the editable text) via a binding. TextField takes a Binding to a String so typing updates your state and vice versa.

Which prefix passes a binding to a control like TextField?

  • #
  • &
  • $ (the projected value)
  • @

Answer: $ (the projected value). The $ prefix turns an @State into the Binding the control reads and writes.

What does Toggle bind to?

  • A String
  • A Double
  • An array
  • A Bool (on/off state) via a binding

Answer: A Bool (on/off state) via a binding. Toggle reads and writes a Bool through its isOn binding.

What is Picker used for?

  • Picking colors only
  • Choosing one option from several, bound to a selection
  • Cropping images
  • Sorting lists

Answer: Choosing one option from several, bound to a selection. Picker lets the user choose one value from a set, bound to a selection property.

What does the selection binding of a Picker track?

  • The chosen option's tag/value
  • The screen brightness
  • The list length
  • Nothing

Answer: The chosen option's tag/value. The selection binding holds the currently chosen option, matched to each option's tag.

What does Stepper bind to and do?

  • A Bool it toggles
  • A numeric value it increments and decrements
  • A String it edits
  • A date only

Answer: A numeric value it increments and decrements. Stepper increases or decreases a bound numeric value with + and − buttons.

Why does a TextField need a binding rather than a plain value?

  • For color
  • So edits flow back into your state (two-way)
  • To make it faster
  • It does not need one

Answer: So edits flow back into your state (two-way). A binding is two-way: the field displays the state and writes the user's edits back to it.

What does the .keyboardType(.emailAddress) modifier do?

  • Validates the email
  • Shows a keyboard suited to typing an email
  • Sends the email
  • Encrypts input

Answer: Shows a keyboard suited to typing an email. .keyboardType configures which on-screen keyboard appears for a TextField.

How do you group related controls within a Form?

  • Section { ... }
  • Group all in a ZStack
  • Use Spacer
  • You cannot group

Answer: Section { ... }. Wrap related controls in a Section to give a Form visually separated groups with optional headers.

Continue this course

Frequently asked questions

What is a Form in SwiftUI?

Form is a container that arranges input controls into a platform-styled, scrollable layout — the look you see in iOS Settings. It automatically styles rows, groups, and section headers, so you focus on the controls rather than the chrome. Inside a Form you place TextField, Toggle, Picker, Stepper, and Sections to group them.

How does TextField update my state?

TextField takes a two-way Binding to a String, which you pass with the $ prefix on an @State property: TextField("Email", text: $email). As the user types, the binding writes into your @State, and if you change the state in code, the field updates to match. That two-way flow keeps the UI and your data in sync.

What do Toggle, Picker, and Stepper bind to?

Toggle binds to a Bool (on/off) via its isOn binding. Picker binds a selection to whichever option's tag matches — choosing an option writes that value into your state. Stepper binds to a numeric value and increments or decrements it with its + and − buttons, optionally constrained to a range.

Why do controls need bindings instead of plain values?

Input controls are two-way: they must display your current state AND write the user's changes back into it. A plain value is a one-way snapshot — the control couldn't update your data. A Binding (passed with $) gives the control read-and-write access to a single source of truth, so edits stay in sync.

How do I group controls and add headers in a Form?

Wrap related controls in a Section, optionally with a header string: Section("Account") { ... }. Sections give a Form visually separated groups with titles and spacing, exactly like grouped settings screens. You can have as many Sections as you need within one Form.

Related lessons