admin管理员组

文章数量:1313243

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:

  • In iOS 18, when @Environment(.dismiss) is used in DetailView, the print("DetailView") statement is triggered twice.
  • The same code works correctly in iOS 17 and earlier, where the print statement is only triggered once, as expected.
  • However, when I remove @Environment(.dismiss) from DetailView, the code works as intended in iOS 18, with the print statement being triggered only once and no duplication of navigation behavior.

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:

  • I’ve confirmed that removing @Environment(.dismiss) solves the issue, and the print statement is triggered only once, and the navigation works as expected in iOS 18 without duplication.
  • The issue only occurs when @Environment(.dismiss) is in use, which seems to be tied to the navigation stack behavior. The code works correctly in iOS 17 and below, where the print statement is only called once.

Expected Behavior:

  • I expect the print("DetailView") statement to be called once when navigating to DetailView, and that the navigation happens only once without duplication. The presence of @Environment(.dismiss) should not cause the navigation to be triggered multiple times.

Questions:

  • Is this a known issue with iOS 18 and SwiftUI navigation? Specifically, is there a new behavior that interacts differently with @Environment(.dismiss)?
  • Has anyone else encountered this problem, and if so, what’s the recommended way to handle it in iOS 18?
  • Is there a workaround to ensure that the navigation doesn’t trigger more than once when using @Environment(.dismiss) in iOS 18?

Any help or insights would be greatly appreciated!

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:

  • In iOS 18, when @Environment(.dismiss) is used in DetailView, the print("DetailView") statement is triggered twice.
  • The same code works correctly in iOS 17 and earlier, where the print statement is only triggered once, as expected.
  • However, when I remove @Environment(.dismiss) from DetailView, the code works as intended in iOS 18, with the print statement being triggered only once and no duplication of navigation behavior.

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:

  • I’ve confirmed that removing @Environment(.dismiss) solves the issue, and the print statement is triggered only once, and the navigation works as expected in iOS 18 without duplication.
  • The issue only occurs when @Environment(.dismiss) is in use, which seems to be tied to the navigation stack behavior. The code works correctly in iOS 17 and below, where the print statement is only called once.

Expected Behavior:

  • I expect the print("DetailView") statement to be called once when navigating to DetailView, and that the navigation happens only once without duplication. The presence of @Environment(.dismiss) should not cause the navigation to be triggered multiple times.

Questions:

  • Is this a known issue with iOS 18 and SwiftUI navigation? Specifically, is there a new behavior that interacts differently with @Environment(.dismiss)?
  • Has anyone else encountered this problem, and if so, what’s the recommended way to handle it in iOS 18?
  • Is there a workaround to ensure that the navigation doesn’t trigger more than once when using @Environment(.dismiss) in iOS 18?

Any help or insights would be greatly appreciated!

Share Improve this question edited Feb 2 at 10:29 koen 5,7417 gold badges58 silver badges101 bronze badges asked Feb 1 at 17:01 Najoua MahiNajoua Mahi 3163 silver badges16 bronze badges 3
  • Stop using the deprecated NavigationLink initialiser. – Sweeper Commented Feb 1 at 17:22
  • SwiftUI can redraw the body whenever it wants – lorem ipsum Commented Feb 1 at 19:31
  • @Sweeper, what are the alternatives? – Najoua Mahi Commented Feb 1 at 23:18
Add a comment  | 

1 Answer 1

Reset to default 0

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 }

本文标签: