DEV Community

Kurt Frey
Kurt Frey

Posted on

Simplify opening and closing sheets in SwiftUI

My pattern up to now was something like this:

import SwiftUI

struct ViewA: View {
    @State private var viewToggle: Bool = false

    var body: some View {
        Button("viewA.openSheet") {
            viewToggle = true
        }
        .sheet(isPresented: $viewToggle) {
            ViewB(viewToggle: $viewToggle)
        }
    }
}

struct ViewB: View {
    @Binding var viewToggle: Bool

    var body: some View {
        NavigationStack {
            Text("View B")
            .toolbar {
                ToolbarItem {
                    Button(action: {
                        viewToggle = false
                    }) {
                        Image(systemName: "xmark")
                    }
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

That meant passing bindings back and forth. But there's a better solution and it has two benefits:

  1. The views become even more reusable, because they do not depend on one another.
  2. The close button is not hardcoded anymore.
import SwiftUI

struct CloseToolbarItem: ToolbarContent {
    @Environment(\.dismiss) private var dismiss

    var body: some ToolbarContent {
        ToolbarItem(placement: .cancellationAction) {
            // Button(role: .close) requires iOS 26+
            Button(role: .close) { dismiss() }
        }
    }
}

struct ViewA: View {
    @State private var viewToggle: Bool = false

    var body: some View {
        Button("viewA.openSheet") {
            viewToggle = true
        }
        .sheet(isPresented: $viewToggle) {
            ViewB()
        }
    }
}

struct ViewB: View {    
    var body: some View {
        NavigationStack {
            Text("View B")
            .toolbar { CloseToolbarItem() }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

With this solution, I got rid of a binding and have a uniform close button on the sheets that implement it.

Top comments (0)