SwiftUI Tutorial · iOS 16+ · iOS 26 Liquid Glass

SwiftUI Form: Sections, Controls & a Settings Screen

Assemble a polished settings screen with Form — grouped and inset styles, sectioned controls, row backgrounds, and LabeledContent for clean read-only rows.

Last updated: 2026-07-17 10 min read By Ahmed Gagan, iOS Engineer
Quick Answer

SwiftUI's Form is a container that lays out labeled controls in platform-standard grouped rows, ideal for settings and data-entry screens. Organize rows with Section, adding header and footer text for context. Drop in Toggle, Picker, Stepper, and TextField and they adopt the correct form styling automatically. Choose the look with .formStyle(.grouped), customize rows with .listRowBackground, and show read-only pairs with LabeledContent.

Purpose
Grouped layout for settings and data entry
Structure
Section(header:footer:) groups rows
Styling
.formStyle(.grouped) and .listRowBackground
Swift Kit support
The Swift Kit ships a complete settings Form with theme, account, and subscription sections.

What a Form gives you for free

A Form is a specialized container that arranges its children as standard grouped rows, matching how Apple's own Settings app looks. Any control you place inside — a Toggle, a Picker, a TextField — automatically takes on form styling, including the label-on-the-left, control-on-the-right layout. This means you write far less layout code than with a hand-built VStack. Forms also handle scrolling, keyboard avoidance, and dynamic type without extra work, which is why they are the default choice for settings and entry screens.

A minimal form
Form {
    Toggle("Notifications", isOn: $notificationsOn)
    Toggle("Haptics", isOn: $hapticsOn)
    Stepper("Reminders: \(count)", value: $count, in: 0...10)
}

Sections with headers and footers

Group related rows with Section, and give each a header to label the group and a footer to explain it. Footers are the idiomatic place for helper text — the small gray sentence under a group in iOS Settings — so use them instead of cramming explanations into row labels. Sections also control visual spacing, breaking a long form into scannable chunks. You can conditionally include a Section with an if statement to show advanced options only when relevant.

A section with header and footer
Form {
    Section {
        Toggle("iCloud Sync", isOn: $syncOn)
    } header: {
        Text("Backup")
    } footer: {
        Text("Your data is encrypted and synced across devices signed in to the same Apple ID.")
    }
}

Form controls that belong inside

Forms shine because the standard controls know how to render inside them. A Picker collapses to a compact menu row with a chevron, a Toggle aligns its switch to the trailing edge, and a Stepper shows its plus and minus buttons on the right. TextField rows sit flush with a subtle divider. Because every control follows the same rules, a form built from these primitives looks native and consistent without any per-row styling on your part.

Mixing controls in one section
Section("Preferences") {
    Picker("Theme", selection: $theme) {
        ForEach(Theme.allCases) { Text($0.label).tag($0) }
    }
    Toggle("Reduce Motion", isOn: $reduceMotion)
    Stepper("Font size: \(fontSize)", value: $fontSize, in: 12...24)
    TextField("Display name", text: $name)
}

Styling: form style and row backgrounds

Control the overall look with .formStyle. The .grouped style gives the classic separated cards, while .columns adapts layouts on macOS. To customize an individual row, apply .listRowBackground for a tinted card or .listRowInsets to adjust padding. Reach for these sparingly — a form's value is its consistency, so heavy per-row customization can make it feel off. On iOS 26, grouped forms inherit the Liquid Glass material automatically when built with standard components.

Grouped style with a highlighted row
Form {
    Section("Plan") {
        LabeledContent("Current", value: "Pro")
        Button("Manage Subscription") { openManage() }
            .listRowBackground(Color.accentColor.opacity(0.12))
    }
}
.formStyle(.grouped)

LabeledContent for read-only rows

Not every row is editable. LabeledContent renders a label paired with a value or an arbitrary trailing view, which is the right tool for showing app version, account email, or a computed total. It handles alignment and truncation the same way system rows do, so your read-only rows match the editable ones. You can pass a plain value string or a full trailing view — an image, a colored status pill — when you need more than text.

Read-only info rows
Section("About") {
    LabeledContent("Version", value: "2.4.1")
    LabeledContent("Account", value: user.email)
    LabeledContent("Status") {
        Text("Active").foregroundStyle(.green)
    }
}

Ship your SwiftUI app in 5 emails

A free 5-part course on the parts that actually stall launches: paywall, auth, onboarding, App Store review, and pricing. No fluff, unsubscribe anytime.

A settings screen you don't have to build

The Swift Kit ships a complete settings Form — theme, account, subscription, and about sections — wired to real state, so you customize instead of starting from scratch.

Get The Swift Kit — $99

Rather have it done for you? I set up and ship your app from $499. See how

How to build a Settings screen with SwiftUI Form

Assemble a real settings screen from sections and standard controls.

  1. 1

    Wrap in NavigationStack

    Put the Form in a NavigationStack and add a navigationTitle so it reads as a Settings screen.

    NavigationStack { Form { ... }.navigationTitle("Settings") }
  2. 2

    Group into sections

    Create a Section per topic — Account, Preferences, About — with a header for each.

    Section("Preferences") { /* rows */ }
  3. 3

    Add controls

    Fill sections with Toggle, Picker, Stepper, and LabeledContent rows bound to state.

    Toggle("Dark Mode", isOn: $darkMode)
  4. 4

    Apply a form style

    Set .formStyle(.grouped) for the classic separated-card appearance.

    .formStyle(.grouped)

Frequently Asked Questions

How do I add a header and footer to a Section in a SwiftUI Form?
Use the Section(header:footer:) trailing-closure form: Section { rows } header: { Text("Backup") } footer: { Text("Explanation") }. The footer is the idiomatic place for the small gray helper text seen in iOS Settings.
How do I change the background color of a form row in SwiftUI?
Apply .listRowBackground to the row, for example .listRowBackground(Color.accentColor.opacity(0.12)). Use it sparingly, since a form's strength is its visual consistency across rows.
How do I show a read-only label and value row in a SwiftUI Form?
Use LabeledContent("Version", value: "2.4.1") for a text pair, or the trailing-closure form to supply a custom view such as a colored status pill. It aligns and truncates like system rows.
What is the difference between grouped and inset form styles in SwiftUI?
The .grouped style renders sections as separated cards with clear spacing, which is the default on iPhone. Inset grouped variants add rounded insets from the screen edges. Set the look with .formStyle(.grouped) or the matching list style.
Why does my Picker look different inside a SwiftUI Form?
Inside a Form a Picker automatically adopts the compact .menu style with a trailing chevron rather than a segmented control. This is expected form styling; override it with an explicit .pickerStyle if you need a different presentation.

Keep exploring

Everything a real app needs, prebuilt

The Swift Kit is a $99 one-time SwiftUI boilerplate with settings, auth, AI, and paywalls already wired — lifetime updates and a 14-day refund.

Get The Swift Kit — $99

One-time purchase · Lifetime updates · 14-day refund