100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Swift

Buttons and User Input in SwiftUI

Learn how SwiftUI captures user intent through Button, Toggle, Slider, Stepper, and TextField, and how actions drive state changes in a declarative UI.

Building UIs with SwiftUIBeginner8 min readJul 8, 2026
Analogies

Buttons and User Input in SwiftUI

SwiftUI provides a family of interactive controls—Button, Toggle, Slider, Stepper, TextField, and TextEditor—that let users generate events which your app responds to by mutating state. Unlike UIKit, where you wire up target-action pairs or delegate callbacks, SwiftUI controls take a closure or a binding directly in their initializer. When the user taps, drags, or types, SwiftUI runs your closure (for stateless actions) or writes into the bound value (for stateful controls), and because SwiftUI views are a function of state, any dependent view automatically re-renders. This closes the loop between input and output without manual view-refresh code.

🏏

Cricket analogy: A batsman's shot (tap) sends the ball toward the boundary, and the scoreboard updates itself automatically because the whole stadium display is wired to the run count, not because someone manually chalks a new number on a board.

The Button View

A Button is initialized with a label (text, an Image, or an arbitrary view builder) and an action closure that runs on tap. Buttons are lightweight: they don't own state themselves, they merely invoke a closure, so all the state they affect must live in an enclosing view or a model object. Button styles—.borderedProminent, .bordered, .plain—change appearance without changing behavior, and role: .destructive or role: .cancel communicate semantic intent to assistive technologies and to system-provided styling.

🏏

Cricket analogy: A captain calling for a bowling change is like tapping a Button: the closure (bringing on the spinner) runs, but the captain doesn't personally hold the match state—the scoreboard and field settings owned elsewhere update in response.

swift
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack(spacing: 16) {
            Text("Count: \(count)")
                .font(.title2)

            HStack {
                Button("Decrement", role: .destructive) {
                    count -= 1
                }
                .buttonStyle(.bordered)

                Button("Increment") {
                    count += 1
                }
                .buttonStyle(.borderedProminent)
            }
        }
        .padding()
    }
}

Bound Controls: Toggle, Slider, Stepper, TextField

Controls like Toggle, Slider, Stepper, and TextField don't take an action closure—they take a Binding to a piece of state and read/write it directly as the user interacts. A Toggle binds to a Bool, a Slider and Stepper bind to a numeric value (often with a in: range and, for Stepper, a step:), and a TextField binds to a String. Because the binding is two-way, the control both displays the current value and updates it, which means the source of truth always lives in exactly one place—the @State (or model property) that owns the binding, referenced with the $ prefix.

🏏

Cricket analogy: A Duckworth-Lewis target display is two-way bound to overs remaining: change the overs and the target recalculates live, just like a Slider bound to a value keeps the displayed number and the underlying state perfectly in sync.

swift
struct PreferencesForm: View {
    @State private var username = ""
    @State private var notificationsEnabled = true
    @State private var volume: Double = 50

    var body: some View {
        Form {
            TextField("Username", text: $username)
                .textInputAutocapitalization(.never)

            Toggle("Enable notifications", isOn: $notificationsEnabled)

            Slider(value: $volume, in: 0...100, step: 1) {
                Text("Volume")
            }
        }
    }
}

Think of a binding as a two-way pipe, not a copy: $username doesn't hand TextField a snapshot of the string, it hands it read/write access to the exact storage cell that owns username. That's why typing in the field is reflected instantly wherever username is read elsewhere in the view.

A common mistake is calling a mutating action directly inside body (e.g. count += 1 outside of a closure). Any state mutation must happen inside an event closure like a button action, .onChange, or .onAppear—mutating state during body evaluation causes undefined behavior and SwiftUI will warn or crash at runtime.

Keyboard, Focus, and Submission

TextField integrates with the keyboard through modifiers like .keyboardType(.emailAddress), .textContentType(.password), and .submitLabel(.done). The @FocusState property wrapper lets you programmatically move focus between fields (useful for 'next field' navigation) and .onSubmit runs a closure when the user presses return or the keyboard's submit button, which is the idiomatic way to trigger a form submission without a separate button tap.

🏏

Cricket analogy: Just as a scorer's app switches to a numeric keypad specifically for entering run totals, a TextField's .keyboardType(.numberPad) presents the right input layout for the data being entered, and .onSubmit is like the scorer pressing 'confirm' to lock in the over's total.

  • Button takes an action closure; Toggle, Slider, Stepper, and TextField take a two-way Binding instead.
  • Use the $ prefix to pass a binding from @State (e.g. $username) into a control.
  • buttonStyle and role (e.g. .destructive) control appearance and communicate semantic intent.
  • State must only be mutated inside closures (actions, .onChange, .onSubmit)—never during body evaluation.
  • @FocusState programmatically manages keyboard focus across multiple input fields.
  • Because views are a function of state, any control update automatically re-renders dependent views.

Practice what you learned

Was this page helpful?

Topics covered

#Swift#IOSWithSwiftUIStudyNotes#MobileDevelopment#ButtonsAndUserInputInSwiftUI#Buttons#User#Input#SwiftUI#StudyNotes#SkillVeris