15

I'm using a NavigationLink inside of a ForEach in a List to build a basic list of buttons each leading to a separate detail screen.

When I tap on any of the list cells, it transitions to the detail view of that cell but then immediately pops back to the main menu screen.

Not using the ForEach helps to avoid this behavior, but not desired.

Here is the relevant code:

struct MainMenuView: View {

    ...

    private let menuItems: [MainMenuItem] = [
        MainMenuItem(type: .type1),
        MainMenuItem(type: .type2),
        MainMenuItem(type: .typeN),
    ]

    var body: some View {
        List {
            ForEach(menuItems) { item in
                NavigationLink(destination: self.destination(item.destination)) {
                    MainMenuCell(menuItem: item)
                }
            }
        }
    }

    // Constructs destination views for the navigation link
    private func destination(_ destination: ScreenDestination) -> AnyView {
        switch destination {
        case .type1:
            return factory.makeType1Screen()
        case .type2:
            return factory.makeType2Screen()
        case .typeN:
            return factory.makeTypeNScreen()
        }
    }
Viktor Malyi
  • 2,298
  • 2
  • 23
  • 40
  • 1
    How does `MainMenuItem` conforms to `Identifiable`? I had a similar issue, where the conformance was implicit. – gujci Oct 09 '19 at 13:05
  • Just by containing "id" property which is derived from UUID().uuidString for each of such menu items. – Viktor Malyi Oct 09 '19 at 13:31
  • 3
    Are they changing when you navigate to the destination. I mean if you have a `@State`, `@Binding` or `@ObservedObject` in `MainMenuView `, the body itself is regenerated which causes the `NavigationLink` to invalidate – gujci Oct 09 '19 at 13:34
  • It totally makes sense, thank you! PS: would you like to post this as an answer so I can accept it? – Viktor Malyi Oct 09 '19 at 13:41
  • I am facing this problem too. However in my case I have a basic foreach wrapper a navigation link, but the auto popping and still happens. Code here : `ForEach(modelA) { eachModel in NavigationLink(destination: Text("Detail view here")) { Text("Press here to go to detail view") } }`. Model A conforms to `Identifiable` and has an `id = UUID()` – user3051673 Apr 01 '20 at 06:48

2 Answers2

14

If you have a @State, @Binding or @ObservedObject in MainMenuView, the body itself is regenerated (menuItems get computed again) which causes the NavigationLink to invalidate (actually the id change does that). So you must not modify the menuItems arrays id-s from the detail view.

If they are generated every time consider setting a constant id or store in a non modifying part, like in a viewmodel.

gujci
  • 1,238
  • 13
  • 21
  • Indeed, the overridden "id" property value was derived from "UUID().uuidString" which resulted in this behavior. – Viktor Malyi Oct 09 '19 at 14:01
  • 1
    you mean `id`? :) – gujci Oct 09 '19 at 14:03
  • in my case, the elements of every menuItems has unique id and did not generated automatically but issue is still here. ``` struct DataType: Identifiable { let id = UUID() var i: Int } @State var dataTypes: [DataType] = { return (0...99).map{ return DataType(i: $0) } }() ``` – muhasturk Jan 15 '20 at 21:11
  • 1
    Looked at it just now. Yes, as the ` @StateObject` and the properties inside it are re-computed, the navigation pops it self out. As, the standard documentation suggests, we are bound to fetch data on the `.onAppear` closure. What would be the best workaround to prevent recurring network calls as the data is already present on the parent view which expects to show the detail view? – Ravi May 03 '21 at 03:32
  • @Ravi have you found a way to work around this? – PipEvangelist Nov 06 '21 at 16:41
  • @PipEvangelist I have not found a way to that. But somehow I found this article today hope it helps. https://www.donnywals.com/understanding-how-and-when-swiftui-decides-to-redraw-views/ (which I am also reading) BTW – Ravi Nov 08 '21 at 00:35
  • @Ravi thanks for the article. I found out that my issue has little to do with networking calls. I had a `onDisappear` to my `NavigationView`, and when I removed it, it worked! – PipEvangelist Nov 08 '21 at 03:32
0

Maybe I found the reason of this bug...

if you use iOS 15 (not found iOS 14), and you write the code NavigationLink to go to same View in different locations in your projects, then this bug appear.

So I simply made another View that has different destination View name but the same contents... then it works..

you can try....

sorry for my poor English...

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 14 '22 at 09:51