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!

NavigationDestination, NavigationLink with List and ForEach Does Not Work
 
 
Q