I’m encountering an issue with SwiftUI navigation in iOS 18, where navigating to a DetailView causes unexpected duplication of navigation behavior when @Environment(.dismiss) is used.
Code Example: Here’s a simplified version of the code:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationStack {
NavigationLink("Go to Detail View", destination: DetailView())
.padding()
}
}
}
struct DetailView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
VStack {
let _ = print("DetailView") // This print statement is triggered twice in iOS 18
}
}
}
Issue:
Alternative Approach with .navigationDestination(for:): I also tested using .navigationDestination(for:) to handle navigation:
struct ContentView: View {
var body: some View {
NavigationStack {
List {
NavigationLink("Mint", value: Color.mint)
NavigationLink("Pink", value: Color.pink)
NavigationLink("Teal", value: Color.teal)
}
.navigationDestination(for: Color.self) { _ in
DetailView()
}
.navigationTitle("Colors")
}
}
}
Even with this alternative approach, the issue persists in iOS 18, where the print statement is triggered twice.
What I've Tried:
Expected Behavior:
Questions:
Any help or insights would be greatly appreciated!
The body
of a view may be fetched any number of times and the print
statement will print its output every time this happens. You shouldn't rely on the body
only being fetched once.
If you want to perform something when the view is first shown, use .onAppear
, or .task
:
VStack {
}
.onAppear {
print("DetailView")
}
EDIT Following from your comment, if the issue is with a child view that shouldn't be shown until the parent is really showing, try setting a flag in .onAppear
and then making the content conditional on this flag. The flag can be reset again in .onDisappear
:
@State private var isShowing = false
VStack {
if isShowing {
// The problem view can be shown here
}
}
.onAppear { isShowing = true }
.onDisappear { isShowing = false }