How it works
DismissAction is a callable value that closes the current presentation — a sheet, popover, cover, or pushed view — without requiring the view to hold a binding to whatever presented it. SwiftUI hands you one through the environment, so a presented view can ask to be dismissed on its own terms instead of coordinating with its presenter through shared state. Reach for it whenever a child view owns a Done or Cancel action and you want it decoupled from the boolean or navigation state that drives the presentation.
Read the action from the environment with @Environment(\.dismiss)
SwiftUI publishes the current dismiss action under the
\.dismissenvironment key, scoped to whatever presentation contains the view. You bind it into a stored property with the@Environmentproperty wrapper, as in@Environment(\.dismiss) private var dismissinsideSheetContent. The view never sees, and never needs, theshowingSheetbinding that the parent used to present it.Call the value directly to dismiss
DismissAction is a struct that conforms to the callable pattern, so you invoke it like a function with no arguments —
dismiss(). That single call tells SwiftUI to dismiss the nearest active presentation, here closing the sheet from inside its ownButton("Dismiss")action closure.Let the presentation context resolve what gets closed
The same
dismiss()call adapts to its surroundings: it closes a sheet, dismisses a popover or fullScreenCover, or pops a view from a NavigationStack, depending on how the view was shown. BecauseSheetContentwas placed by.sheet(isPresented:), callingdismissresolves to dismissing that sheet — no code in the child needs to know which presentation style was used.Keep presenter and content decoupled
Since the action travels through the environment, the presenting view keeps ownership of its own state —
@State private var showingSheetlives only inDismissActionDemo— while the presentedSheetContentstays self-contained and reusable across different presentation sites. Dismissing flips the presenter's state back for you without the child touching it.
Button("Dismiss") action closure with print("tapped") instead of dismiss(), and the sheet stays open on tap — confirming it is the dismiss() call, not the button itself, that closes the presentation.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct DismissActionDemo: View {
@State private var showingSheet = true
var body: some View {
Text("Main screen")
.padding()
.sheet(isPresented: $showingSheet) {
SheetContent()
}
}
}
struct SheetContent: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(spacing: 16) {
Text("Sheet Presented")
.font(.headline)
Button("Dismiss") {
dismiss()
}
.buttonStyle(.borderedProminent)
}
.padding()
}
}