SwiftUI.cc
Snippets
PresentationControls ModifiersIntermediate

SwiftUI Sheets, Popovers, and Detents

Present focused secondary work with sheets, popovers, and detents while keeping dismissal and size behavior explicit.

5 min readUpdated 2026-06
Editable settings sheet
import SwiftUI

struct AccountView: View {
    @State private var editing = false

    var body: some View {
        Button("Edit Settings") { editing = true }
            .sheet(isPresented: $editing) {
                SettingsEditor()
                    .presentationDetents([.medium, .large])
                    .presentationDragIndicator(.visible)
            }
    }
}

struct SettingsEditor: View {
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        NavigationStack {
            Form { Toggle("Email updates", isOn: .constant(true)) }
                .navigationTitle("Settings")
                .toolbar { Button("Done") { dismiss() } }
        }
    }
}
Use this when

The user needs a short task without losing the current screen context.

The presented content has its own title, controls, or save/cancel flow.

You want medium and large heights for a bottom sheet experience.

Avoid this when

The content is primary navigation; push a destination instead.

The sheet contains many nested routes and becomes a second app.

Dismissal would lose unsaved work without a guard.

Implementation notes

Use item-based presentation when the sheet content depends on selected data.
Detents make sheets feel less heavy, but the content still needs to work at each selected height.
If users can lose work, combine a disabled interactive dismissal with an explicit cancel or save action.

Checklist

Test the sheet at every detent you expose.

Put dismiss logic inside the presented view when it owns the completion flow.

Check compact-width adaptation for popovers.

Related reference