Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - Navigation bar button not clickable after sheet has been presented

I have just started using SwiftUI a couple of weeks ago and i'm learning. Today I ran into a into an issue.

When I present a sheet with a navigationBarItems-button and then dismiss the ModalView and return to the ContentView I find myself unable to click on the navigationBarItems-button again.

My code is as follows:

struct ContentView: View {

    @State var showSheet = false

    var body: some View {
        NavigationView {
            VStack {
                Text("Test")
            }.sheet(isPresented: self.$showSheet) {
                ModalView()
            }.navigationBarItems(trailing:
                Button(action: {
                    self.showSheet = true
                }) {
                    Text("SecondView")
                }
            )
        }
    }
}

struct ModalView: View {

    @Environment(\.presentationMode) var presentation

    var body: some View {
        VStack {
            Button(action: {
                self.presentation.wrappedValue.dismiss()
            }) {
                Text("Dismiss")
            }
        }
    }
}
like image 315
Markus Moltke Avatar asked Oct 22 '19 21:10

Markus Moltke


4 Answers

I think this happens because the presentationMode is not inherited from the presenter view, so the presenter didn't know that the modal is already closed. You can fix this by adding presentationMode to presenter, in this case to ContentView.

struct ContentView: View {

    @Environment(\.presentationMode) var presentation
    @State var showSheet = false

    var body: some View {
        NavigationView {
            VStack {
                Text("Test")
            }.sheet(isPresented: self.$showSheet) {
                ModalView()
            }.navigationBarItems(trailing:
                Button(action: {
                    self.showSheet = true
                }) {
                    Text("SecondView")
                }
            )
        }
    }
}

Tested on Xcode 12.5.

Here is the full working example.

like image 63
Vahagn Gevorgyan Avatar answered Oct 19 '22 20:10

Vahagn Gevorgyan


This seems to be a bug in SwiftUI. I am also still seeing this issue with Xcode 11.5 / iOS 13.5.1. The navigationBarMode didn't make a difference.

I filed an issue with Apple:


FB7641003 - Taps on a navigationBarItem Button presenting a sheet sometimes not recognized

You can use the attached example project SwiftUISheet (also available via https://github.com/ralfebert/SwiftUISheet) to reproduce the issue. It just presents a sheet from a navigation bar button. Run the app and tap repeatedly on the 'plus' button in the nav bar. When the sheet pops up, dismiss it by sliding it down. Only some taps to the button will be handled, often a tap is ignored.

Tested on Xcode 11.4 (11E146) with iOS 13.4 (17E255).

like image 30
Ralf Ebert Avatar answered Oct 19 '22 19:10

Ralf Ebert


Very hacky but this worked for me:

Button(action: {
    self.showSheet = true
}) {
    Text("SecondView")
    .frame(height: 96, alignment: .trailing)
}
like image 4
adamwjohnson5 Avatar answered Oct 19 '22 21:10

adamwjohnson5


I'm still seeing this issue with Xcode 13 RC and iOS 15. Unfortunately the solutions above didn't work for me. What I ended up doing is adding a small Text view to the toolbar whose content changes depending on the value of the .showingSheet property.

struct ContentView: View {
    @State private var showingSheet = false

    var body: some View {
        NavigationView {
            VStack {
                Text("Content view")
                Text("Swift UI")
            }
            .sheet(isPresented: $showingSheet) {
                Text("This is a sheet")
            }
            .navigationTitle("Example")
            .toolbar {
                ToolbarItemGroup(placement: .navigationBarTrailing) {
                    // Text view workaround for SwiftUI bug
                    // Keep toolbar items tappable after dismissing sheet
                    Text(showingSheet ? " " : "").hidden()
                    Button(action: {
                        self.showingSheet = true
                    }) {
                        Label("Show Sheet", systemImage: "plus.square")
                    }
                }
            }
        }
    }
}

I realize it's not ideal but it's the first thing that worked for me. My guess is that having the Text view's content change depending on the .showingSheet property forces SwiftUI to fully refresh the toolbar group.

like image 4
joltguy Avatar answered Oct 19 '22 19:10

joltguy