2

SwiftUI has unexpected behaviors with .sheet that I haven't been able to work out. Here is a simple Master > Detail View that uses the same modal. One in a NavigationView the other in .navigationBarItems:

struct MasterView: View {
    @State var showModal: Bool = false
    var modal: some View {
        ModalView(showModal: $showModal)
    }
    var body: some View {
        NavigationView {
            VStack {
                Button("Can be dismissed") { self.showModal = true }.sheet(isPresented: $showModal) { self.modal }
                NavigationLink(destination: DetailView()) { Text("Can't be dismissed") }
            }
        }
    }
}

struct DetailView: View {
    @State var showModal: Bool = false
    var modal: some View {
        ModalView(showModal: $showModal)
    }
    var body: some View {
        Text("Detail View")
            .navigationBarItems(trailing: Button("Dismisss?") { self.showModal = true }.sheet(isPresented: $showModal) { self.modal })
    }
}

struct ModalView: View {
    @Binding var showModal: Bool
    var body: some View {
        VStack {
            Text("Modal View")
            Button("Dismiss") { self.showModal = false }
        }
    }
}

The issue is that, though ModalView can be dismissed from the Button in MasterView it cannot be dismissed with the .navigationBarItems in DetailView

Anyone know how to dismiss a .sheet invoked from .navigationBarItems?

EDIT: Interestingly if you switch

 Text("Detail View")

with

Button("Can Dismiss") { self.showModal = true }.sheet(isPresented: $showModal) { self.modal }

You get very unexpected results, causing the .navigationBarItems to be disabled unless the modal was dismissed via a drag gesture

EDIT 2: This behavior has now been filed as a bug with id: FB6891155

Brandon Bradley
  • 3,160
  • 4
  • 22
  • 39

1 Answers1

2

This definitely warrants a bug report. Initially I thought that using @Environment(\.presentationMode) might solve this issue but it does absolutely nothing to change the behavior.

What I did find is that you can get the modal to be dismissible if you only have 1 sheet. I.e. if you delete the sheet call from your DetailView.

struct MasterView: View {
    @State var showModal: Bool = false

    var modal: some View {
        ModalView()
    }
    var body: some View {
        NavigationView {
            VStack {
                Button("Can be dismissed") { self.showModal = true }.sheet(isPresented: $showModal) { self.modal }
                NavigationLink(destination: DetailView(showModal: $showModal)) { Text("Can't be dismissed") }
            }
        }
    }
}

struct DetailView: View {
    @Binding var showModal: Bool

    var body: some View {
        Text("Detail View")
            .navigationBarItems(trailing: Button("Dismisss?") { self.showModal = true })
    }
}

struct ModalView: View {
    @Environment(\.presentationMode) var showModal

    var body: some View {
        VStack {
            Text("Modal View")
            Button("Dismiss") {
                self.showModal.value.dismiss()
            }
        }
    }
}

While this does make the modal programmatically dismissible, you do end up encountering the second bug that you referred to, which is that the .navigationBarItems is disabled until you go back SwiftUI button in navigation bar only works once

video of code in action

Zain
  • 1,569
  • 1
  • 13
  • 19