Xcode: Version 11.3 (11C29)
Target: Simulator running iOS 13.3
I'm getting inconsistent behaviour that's hard to diagnose when working with sheets and SwiftUI. When I:
* Use a button in the NavigationView
itself, I can consistently call up the sheet and dismiss it
* Use a button in the bar of the NavigationView
(i.e. navigationBarItems
), the behaviour is inconsistent: sometimes it toggles without issue, sometimes it will "lock up" and not respond for ~10 seconds before functioning again. Swiping around and performing other actions in the interface seem to help in "resetting" the functionality.
Minimum reproducible example:
import SwiftUI
struct ContentView: View {
@State var isModalShowing = false
var body: some View {
NavigationView {
VStack {
List {
ForEach(1..<5) { number in
Text(String(number))
}
}
Button("First Modal Button") {
self.isModalShowing = true
}
}
.navigationBarItems(leading:
Button("Second Modal Button") {
self.isModalShowing = true
})
}
.sheet(isPresented: $isModalShowing) {
TestView(isPresented: self.$isModalShowing)
}
}
}
struct TestView: View {
@Binding var isPresented: Bool
var body: some View {
VStack {
Text("Hello SwiftUI!")
Button("Dismiss") {
self.isPresented = false
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
To note:
* Pressing First modal button
works consistently
* Pressing Second modal button
works inconsistently after dismissing the modal for the first time (try using this button ~5-10 times in a row, it should lock up, but it's hard to predict when it will)
There's already a few StackOverflow questions/answers about this, but none have worked for me so far and they seem to talk about the Xcode Betas, whereas I am on stable (hopefully 11.3 is stable). Different ways of passing the state (@Binding
vs @Environment
) didn't help. Moving around where the .sheet
is called also didn't change anything. I think it's also related to the List
and/or ForEach
but I'm new to Swift and not sure how to debug further.
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):
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:
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:
So that means, you now have two options to consider:
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With