Swiftui Modal Presentation Works Only Once from Navigationbaritems

SwiftUI modal presentation works only once from navigationBarItems

Well, the issue is in bad layout (seems broken constrains) of navigation bar button after sheet has closed

It is clearly visible in view hierarchy debug:

demo

Here is a fix (workaround of course, but safe, because even after issue be fixed it will continue working). The idea is not to fight with broken layout but just create another button, so layout engine itself remove old-bad button and add new one refreshing layout. The instrument for this is pretty known - use .id()

demo2

So modified code:

struct ContentView: View {

@State var showSheetView = false
@State private var navigationButtonID = UUID()

var body: some View {
NavigationView {
Group {
Text("Master")
Button(action: { self.showSheetView.toggle() }) {
Text("Button 1")
}
}
.navigationBarTitle("Main")
.navigationBarItems(trailing: Button(action: {
self.showSheetView.toggle()
}) {
Text("Button 2").bold() // recommend .padding(.vertical) here
}
.id(self.navigationButtonID)) // force new instance creation
}
.sheet(isPresented: $showSheetView) {
DetailView(isPresented: self.$showSheetView)
.onDisappear {
// update button id after sheet got closed
self.navigationButtonID = UUID()
}
}
}
}

SwiftUI button in navigation bar only works once

It's a known bug related to navigation bar items and not relegated to just sheets, it seems to affect any modal, and I've encountered it in IB just the same when using modal segues.

Unfortunately this issue is still present in 11.3 build, hopefully they get this fixed soon.

SwiftUI Sheets: Inconsistent sheet behaviour with navigationBarItems

I believe this is a bug. Though my answer isn't going to solve this problem, but can give you options to consider.

Out of curiosity, I did some debugging of the view hierarchy. With the current setup, initially the navigation bar button stays here (highlighted as blueish color):

Sample Image

Then you present the sheet and do the dismissal of the sheet accordingly. After the dismissal, the position of the navigation bar button isn't quite right. See:

Sample Image

The Text of the button stays in the right position, but the bar button is displaced from where it should be.

But this displacement doesn't happen if you use NavigationLink instead of .sheet presentation.

Well, this issue is prevalent for the large option of TitleDisplayMode of navigation bar title, and it's the default for a quite long. But if the inline option is used, the issue doesn't exist. See, with the inline option, before and after the sheet dismissal the navigation bar button stays at the same place:

Sample Image


So that means, you now have two options to consider:

  1. Use NavigationLink instead of sheet presentation. You can do this by placing the below code inside the outermost View (in your case the VStack):

    NavigationLink(destination: TestView(), isActive: self.$isModalShowing) {
    EmptyView()
    // no need to provide any other view as it will be triggered by the action
    // of navigation bar button item which already provides its own view
    }

Note: This NavigationLink option isn't tested with Xcode 11.3 or newer as navigation link seems to misbehave with this version. But works as expected upto Xcode 11.2.1. More on SwiftUI unable to navigate back and forth with navigationLink


  1. Use the inline option of the navigation bar TitleDisplayMode as:

    .navigationBarTitle(Text("Home"), displayMode: .inline)
    // you can get rid of the title "Home" with empty "" string though

Can a Modal Sheet have a Navigation Bar in SwiftUI?

You have to wrap your modal view in a NaviagtionView like this

@State var isModalSheetShown: Bool = false

var body: some View {
VStack {
Text("Main")
}
.navigationBarItems(trailing: Button("Add",
action: { self.isModalSheetShown = true }))
.sheet(isPresented: $isModalSheetShown) {
NavigationView {
VStack {
Text("Modal")
}
.navigationBarItems(trailing: Button("Done",
action: {}))
}
}
}

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

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.



Related Topics



Leave a reply



Submit