NavigationLink problem when using it in a ForEach

Viewed 66

I am building an app which displays a planning of matches. Each day containing at least one match create a group with the date and the matches. Those matches are embedded in a NavigationLink to access to a detail view. Here an example of the layout

The issue occurs when I click on a match that is part of a day with multiple matches. After having clicked on a match, a message appears in the console "SwiftUI encountered an issue when pushing aNavigationLink. Please file a bug." and it shows me the destination view. But when I go back to the root view, I can't click on any matches.

If I click on a match in a day with only one match, I can click on other matches.

Here is my code :

PlanningMatchView :

ZStack() {
    HStack() {
        Image(systemName: "testtube.2")
        Spacer()
        Image(systemName: "testtube.2")
        Text("Match \(id)")
            .foregroundColor(.white)
        Image(systemName: "testtube.2")
        Spacer()
    }
    .padding(.init(top: 0, leading: 15, bottom: 0, trailing: 15))
}
.frame(height: 50)
.background(matchBackgroundColor)

PlanningDayView :

VStack(alignment: .center, spacing: 0) {
    HStack {
        Text(jour.date)
            .bold()
            .foregroundColor(.white)
            .frame(height: 50)
        .background(.clear)
        Spacer()
    }
    .padding(.leading, 12)
    Divider()
        .frame(height: 1)
        .overlay(lineColor)
    ForEach(jour.matches, id: \.self) { match in
        ZStack {
            NavigationLink(destination: MatchDetailView(matchId: match.id)) {
                PlanningMatchView(id: match.id, hour: match.hour, game: match.game, team1: match.team1, team2: match.team2)
            }
            .padding(.init(top: 0, leading: 0, bottom: 0, trailing: 15))
        }
        Divider()
            .frame(width: 300, height: 0.5)
            .overlay(lineColor)
    }
}
.background(matchBackgroundColor)
.cornerRadius(7)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.padding(.init(top: 0, leading: 0, bottom: 8, trailing: 0))

HomeView : for now I'm taking the matches from a Match array

NavigationView {
    ZStack {
        Color("backgroundColor").edgesIgnoringSafeArea(.all)
        List {
            PlanningDayView(jour: Jour(date: "Mercredi 23 juillet", matches: [Matches[1]]))
            PlanningDayView(jour: Jour(date: "Dimanche 27 juillet", matches: [Matches[2], Matches[3]]))
            PlanningDayView(jour: Jour(date: "Mercredi 30 juillet", matches: [Matches[4]]))
            PlanningDayView(jour: Jour(date: "Mardi 02 août ", matches: [Matches[5], Matches[6]]))
            PlanningDayView(jour: Jour(date: "Mardi 09 août", matches: [Matches[7]]))
            PlanningDayView(jour: Jour(date: "Jeudi 11 août", matches: [Matches[1], Matches[2]]))
        }
        .listStyle(PlainListStyle())
        .navigationTitle("Planning")
        .foregroundColor(.white)
    }
}

ContentView :

var body: some View {
    TabView {
        HomeView()
            .tabItem {
                Image("planning.unselected")
                    .renderingMode(.template)
                Text("Planning")
            }
        RankView()
            .tabItem {
                Image("classement")
                    .renderingMode(.template)
                Text("Classement")
            }
    }
}

I hope I've been clear in all my explanations.

2 Answers

That's not really how List works. A List is separated into Sections and Rows but you are treating a row like a section and making the PlanningDayView with custom rows and dividers.

Therefore each PlanningDayView is a single row, but a NavigationLink always takes the entire row so multiple are not possible.

You have two options:

  1. Go the List way and simply remove a lot of the custom handling you currently have
  2. Keep the custom handling, but then you shouldn't use List but a ScrollView

This would be a minimal List way of what you are trying to do I think. I restructured the PlanningDayView into a Section. Then each NavigationLink is a separate row and therefore works just as expected.

struct PlanningDayView: View {
    let jour: Jour
        
    var body: some View {
        Section {
            Text(jour.date)
                .bold()
                .foregroundColor(.white)
            
            ForEach(jour.matches, id: \.self) { match in
                NavigationLink {
                    MatchDetailView(matchId: match.id)
                } label: {
                    PlanningMatchView(id: match.id,
                                      hour: match.hour,
                                      game: match.game,
                                      team1: match.team1,
                                      team2: match.team2)
                }
            }
        }
        .listRowBackground(matchBackgroundColor)
        .listRowSeparatorTint(lineColor)
    }
}

Instead of .listStyle(PlainListStyle()) on your HomeView you would need to change it to .listStyle(.insetGrouped) to regain the rounded corner styling you currently have

replace your List in HomeView, with ScrollView, works for me.

Related