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")
}
}
}
}
}
}
That meant passing bindings back and forth. But there's a better solution and it has two benefits:
- The views become even more reusable, because they do not depend on one another.
- 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() }
}
}
}
With this solution, I got rid of a binding and have a uniform close button on the sheets that implement it.
Top comments (0)