NavigationDestination, NavigationLink with List and ForEach Does Not Work

Dear community,

I am a new developer and I am building a view (called Root) that has a list of rows where clicking each row navigates to a completely different view.

I have a CaseIteratable enum and I list each enum type using ForEach and each enum case navigates to a different view using NavigationLink and NavigationDestination. But the problem is that clicking any of the rows for the first time navigates correctly to the corresponding view. But when I go back to the root view and chose another row, it navigates me to a blank view for less than a sec and automatically navigates back to the root view.

Below is the code for reference.

I would really appreciate some help and advice here. Thank you very much!

struct RootViewNavigationStack: View {
    @AppStorage("items") private var items = Item.allCases
    @State private var enableMove = false
    
    @State private var rootStackPath: NavigationPath = .init()
    
    var body: some View {
        NavigationStack(path: $rootStackPath) {
            VStack {
                List {
                    ForEach(items) { item in
                        HStack {
                            NavigationLink(value: item) {
                                ListCell(
                                    icon: item.icon,
                                    title: item.title)
                            }
                            .disabled(enableMove)
                            
                            if enableMove {
                                withAnimation {
                                    Image(systemName: "line.3.horizontal")
                                        .foregroundStyle(.secondary)
                                }
                            }
                        }
                        
                    }
                    .onMove(perform: enableMove == true ? moveItems : nil)
                }
            }
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        enableMove.toggle()
                    } label: {
                        if enableMove {
                            Text("Done")
                                .bold()
                        } else {
                            Text("Edit")
                        }
                    }
                }
            }
            .navigationDestination(for: Item.self) { item in
                item.destinationView
            }
            .navigationTitle("Root")
        }
    }
}

and this is the Item enum for more info

Just kindly ignore the var iconName since it doesnt represent any actual SF Symbol name

enum Item: Identifiable, Codable, Hashable, CaseIterable {
    case view1
    case view2
    case view3
    case view4
    case view5
    
    var id: Item { self }
}

extension Item {
    var title: String {
        switch self {
        case .view1:
            "View1"
        case .view2:
            "View2"
        case .view3:
            "View3"
        case .view4:
            "View4"
        case .view5:
            "View5"
        }
    }
    
    var iconName: String {
        switch self {
        case .view1:
            "View1"
        case .view2:
            "View2"
        case .view3:
            "View3"
        case .view4:
            "View4"
        case .view5:
            "View5"
        }
    }
    
    var icon: Image {
        Image(systemName: self.iconName)
    }
    
    @ViewBuilder
    var destinationView: some View {
        switch self {
        case .view1:
            CarView()
        case .view2:
            HouseView()
        case .view3:
            MusicView()
        case .view4:
            ShoesView()
        case .view5:
            BooksView()
        }
    }
}

Once again, would really appreciate someone to help and many thanks 🙏!

Answered by eugeneee in 793959022

Hi @darkpaw,

Thank you for helping. The moveItems is just a function as shown below.

private func moveItems(indices: IndexSet, newOffset: Int) {
        self.items.move(fromOffsets: indices, toOffset: newOffset)
}

I've shown all the code but it doesn't work for me. But I think I've found the problem, all the views like CarView(), HouseView() have their own NavigationStack and I didn't pass in the NavigationPath binding.

Thanks

You haven't defined what this is:

.onMove(perform: enableMove == true ? moveItems : nil)

and so I have to comment out that line.

The code works fine for me. I don't see any weirdness. If I select the first view, CarView, and go back, then select HouseView, I see HouseView.

Is there something else in your code that you aren't showing us?

Accepted Answer

Hi @darkpaw,

Thank you for helping. The moveItems is just a function as shown below.

private func moveItems(indices: IndexSet, newOffset: Int) {
        self.items.move(fromOffsets: indices, toOffset: newOffset)
}

I've shown all the code but it doesn't work for me. But I think I've found the problem, all the views like CarView(), HouseView() have their own NavigationStack and I didn't pass in the NavigationPath binding.

Thanks

@eugeneee, nesting NavigationStacks is not supported through iOS 18. Further, nested NavigationStacks with shared paths can cause infinite loops. I would recommend removing the NavigationStack from any views pushed via the navigationDestination.

HI @Frameworks Engineer, thanks for pointing out. The reason for me doing so is to display the navigation title, but seems like it is wrong and unnecessary. All gud now, thanks!