SwiftUI does not manage two NavigationLinks in a single list row

There seems to be a problem with placing multiple NavigationLinks on a List item row in SwiftUI. If I have two links on the same row and tap one of them, SwiftUI seems to create a path using both links. Irrespective of which of the two is tapped, the view specified by the first (left-hand) appears. Tapping Back reveals the view specified by the second (right-hand) link. Tapping again returns to the list.

This problem has been mentioned before in relation to Buttons. The solution offered was to use button styles, and make sure the button areas do not overlap (how could they overlap?). This does not work

ChatGPT gave me a clear example, which it claimed would work, but just demonstrates the problem

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                ForEach(0..<10) { index in
                    HStack {
                        NavigationLink(destination: DestinationView1(item: index)) {
                            Text("Navigate to View 1")
                                .padding()
                                .background(Color.blue)
                                .foregroundColor(.white)
                                .cornerRadius(8)
                        }
                        .buttonStyle(PlainButtonStyle()) // Ensure the link only activates on its area

                        Spacer()

                        NavigationLink(destination: DestinationView2(item: index)) {
                            Text("Navigate to View 2")
                                .padding()
                                .background(Color.green)
                                .foregroundColor(.white)
                                .cornerRadius(8)
                        }
                        .buttonStyle(PlainButtonStyle()) // Ensure the link only activates on its area
                    }
                    .padding(.vertical, 5)
                }
            }
            .navigationBarTitle("Navigation Links")
        }
    }
}

struct DestinationView1: View {
    var item: Int

    var body: some View {
        Text("Destination View 1 for item \(item)")
            .navigationBarTitle("View 1", displayMode: .inline)
    }
}

struct DestinationView2: View {
    var item: Int

    var body: some View {
        Text("Destination View 2 for item \(item)")
            .navigationBarTitle("View 2", displayMode: .inline)
Answered by Claude31 in 789289022

I may have found a simple solution.

Problem comes from List which manages interactions with rows on its own, creating the mess.

I just replaced List by a ScrollView, to get very similar display (except the ">" after button, but works OK for each link.

No need either for buttonStyle.

struct ContentView: View {
    
    var body: some View {
        NavigationView {
                //            List {
                ScrollView {
                ForEach(0..<10) { index in
                    HStack {
                        Spacer()

                        NavigationLink(destination: DestinationView1(item: index)) {
                            Text("Navigate to View 1")
                                .padding()
                                .background(Color.blue)
                                .foregroundColor(.white)
                                .cornerRadius(8)
                        }
                        // .buttonStyle(PlainButtonStyle()) // Not needed

                        Spacer()

                        NavigationLink(destination: DestinationView2(item: index)) {
                            Text("Navigate to View 2")
                                .padding()
                                .background(Color.green)
                                .foregroundColor(.white)
                                .cornerRadius(8)
                        }
                        // .buttonStyle(PlainButtonStyle()) // Not needed

                        Spacer()
                    }
                    .padding(.vertical, 5)
                }
            }
            .navigationBarTitle("Navigation Links")
        }
    }
}

struct DestinationView1: View {
    var item: Int

    var body: some View {
        Text("Destination View 1 for item \(item)")
            .navigationBarTitle("View 1", displayMode: .inline)
    }
}

struct DestinationView2: View {
    var item: Int

    var body: some View {
        Text("Destination View 2 for item \(item)")
            .navigationBarTitle("View 2", displayMode: .inline)
    }
}

It is really difficult and risky to try this way (and Chat GPT is not clever enough to find it 😉).

So, I would try to replace your navigation links by buttons and use ontapGesture to detect taps.

Some more help here: https://stackoverflow.com/questions/72917698/multiple-navigation-destinations-from-a-list-row-in-swiftui

Hi Claude31, thank you so much for taking the time to reply. I have tried changing to buttons, but it was not successful, as described in another report "Swiftui - Pressing button in a list also actions another button", which you also responded to, suggesting using button style, (which does not fix it). The same problem exists with buttons. I included the ChatGPT code because it illustrates the problem much more concisely than my own code. I really think this is a flaw in SwiftUI, although I am wary of making such a claim. Please could you paste the code above into Xcode and witness the failure. It will only take a minute. I would love to see this code example reach the Swift team. I think they would be surprised, and maybe able to resolve it. Best regards, Denis Stanton

Accepted Answer

I may have found a simple solution.

Problem comes from List which manages interactions with rows on its own, creating the mess.

I just replaced List by a ScrollView, to get very similar display (except the ">" after button, but works OK for each link.

No need either for buttonStyle.

struct ContentView: View {
    
    var body: some View {
        NavigationView {
                //            List {
                ScrollView {
                ForEach(0..<10) { index in
                    HStack {
                        Spacer()

                        NavigationLink(destination: DestinationView1(item: index)) {
                            Text("Navigate to View 1")
                                .padding()
                                .background(Color.blue)
                                .foregroundColor(.white)
                                .cornerRadius(8)
                        }
                        // .buttonStyle(PlainButtonStyle()) // Not needed

                        Spacer()

                        NavigationLink(destination: DestinationView2(item: index)) {
                            Text("Navigate to View 2")
                                .padding()
                                .background(Color.green)
                                .foregroundColor(.white)
                                .cornerRadius(8)
                        }
                        // .buttonStyle(PlainButtonStyle()) // Not needed

                        Spacer()
                    }
                    .padding(.vertical, 5)
                }
            }
            .navigationBarTitle("Navigation Links")
        }
    }
}

struct DestinationView1: View {
    var item: Int

    var body: some View {
        Text("Destination View 1 for item \(item)")
            .navigationBarTitle("View 1", displayMode: .inline)
    }
}

struct DestinationView2: View {
    var item: Int

    var body: some View {
        Text("Destination View 2 for item \(item)")
            .navigationBarTitle("View 2", displayMode: .inline)
    }
}

Brilliant! Strangely the button appearance changes, but that is probably because List has some default appearance settings that ScrollView doesn't bother with. I have already redesigned this part of the app to avoid having two navigation links on one item, but your solution may help with another part of the app where tapping a link inside a List sometimes generates a long navigation path, apparently using the ForEach to build a path using every link inside the list, not just the one tapped, so the Back button has to be clicked maybe 20 times to get back the list view. May I ask, are you on the SwiftUI team?

Thanks for the feedback. What did change in appearance (just for my understanding). And, no, I'm not in SwiftUI team, I wish I were, I would be much more knowledgable… Have a good day and good continuation.

List on the left vs ScrollView on the right. No other change to code

SwiftUI does not manage two NavigationLinks in a single list row
 
 
Q