Reputation: 466
I am working with sheets in SwiftUI and recently found a problem with navigation while a sheet is already open.
In the code below i have a navigation stack that navigates to two different detail view:
struct ContentView: View {
@State var navigationPath = NavigationPath()
var body: some View {
NavigationStack(path: $navigationPath) {
VStack {
Text("Go to first detail")
Button("Submit") {
navigationPath.append("FirstDetail")
}
}
.padding()
.navigationDestination(for: String.self) { value in
if value == "FirstDetail" {
FirstDetailView(path: $navigationPath)
}
if value == "SecondDetail" {
SecondDetailView()
}
}
}
}
}
In the code below is my first detail view that already present sheet and also has a button to navigate to the second detail view:
struct FirstDetailView: View {
@Binding var path: NavigationPath
@State var isPresented = true
var body: some View {
VStack {
Text("Hello First!")
Button("GoToSecond", action: {
path.append("SecondDetail")
})
Spacer()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.yellow)
}
.sheet(isPresented: $isPresented) {
SheetDetailView(title: "First")
.presentationDetents([.medium, .large])
.presentationBackgroundInteraction(.enabled)
}
}
}
also in my second detail view, I have another sheet:
struct SecondDetailView: View {
@State var isPresented = true
var body: some View {
VStack {
Text("Hello Second")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.green)
}
.sheet(isPresented: $isPresented) {
SheetDetailView(title: "Second")
.presentationDetents([.medium, .large])
.interactiveDismissDisabled()
.presentationBackgroundInteraction(.enabled)
}
}
}
And here is my sheet detail view:
struct SheetDetailView: View {
var title: String
var body: some View {
Text("Hello \(title)")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.red)
}
}
The result will be like this:
So my issue is that navigation to another view while the first sheet is open causes this sheet to appear in the second detail view so each of detailview first and second has its own sheet, and also I want to know what is the best approach for this issue.
Upvotes: 1
Views: 608
Reputation: 119917
It depends on the desired output, a simple way would delay the presentation:
isPresented
value equal to false
true
with a delaySo you will have:
Changed code:
struct FirstDetailView: View {
@Binding var path: NavigationPath
@State var isPresented = false // 👈 Default this to false
var body: some View {
VStack {
Text("Hello First!")
Button("GoToSecond", action: {
path.append("SecondDetail")
isPresented = false // 👈 You can ignore this to have different appearance
})
Spacer()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.yellow)
}
.sheet(isPresented: $isPresented) {
SheetDetailView(title: "First")
.presentationDetents([.medium, .large])
.presentationBackgroundInteraction(.enabled)
}
.task {
try? await Task.sleep(for: .seconds(0.55)) // 👈 Wait for the transition
isPresented = true // 👈 Present the sheet
}
}
}
struct SecondDetailView: View {
@State var isPresented = false // 👈 Default this to false
var body: some View {
VStack {
Text("Hello Second")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.green)
}
.sheet(isPresented: $isPresented) {
SheetDetailView(title: "Second")
.presentationDetents([.medium, .large])
.interactiveDismissDisabled()
.presentationBackgroundInteraction(.enabled)
}
.task {
try? await Task.sleep(for: .seconds(0.55)) // 👈 Wait for the transition
isPresented = true // 👈 Present the sheet
}
}
}
Upvotes: 1
Reputation: 2093
The only way I've found to make the current sheet close and the next one open is by doing some trickery with DispatchQueses:
struct FirstDetailView: View {
@Binding var path: NavigationPath
@State var isPresented = true
var body: some View {
VStack {
Text("Hello First!")
Button("GoToSecond", action: {
isPresented = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
path.append("SecondDetail")
}
})
Spacer()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.yellow)
}
.sheet(isPresented: $isPresented, content: {
SheetDetailView(title: "First")
.presentationDetents([.medium, .large])
.presentationBackgroundInteraction(.enabled)
})
}
}
struct SecondDetailView: View {
@State var isPresented = false
var body: some View {
VStack {
Text("Hello Second")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.green)
}
.sheet(isPresented: $isPresented, content: {
SheetDetailView(title: "Second")
.presentationDetents([.medium, .large])
.interactiveDismissDisabled()
.presentationBackgroundInteraction(.enabled)
})
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
isPresented = true
}
}
}
}
I've been seeing weird glitchy stuff recently on SwiftUI behalf. Let me know if this solution is good enough for you!
Upvotes: 0