Search code examples
swiftuiforeachnavigation

Why are my destination links only binding the last contact as the destination link's data?


I am iterating through an array of Contacts to display a cell for each contact in a grid. Please see my code:

ForEach($contacts, id: \.self) { $contact in
                        // Creating a grid item for each contact
                        ContactCell(contact: contact)
                            .navigationDestination(isPresented: $shouldPresentContactMainView) {
                                ContactMainView(contact: $contact)
                            }
                            .onTapGesture {
                                shouldPresentContactMainView.toggle()
                            }
                    }

What's weird is that, all of the grid items display the correct cell for each contact. However, when I tap on one of them, it segues, then displays the data of the last contact of the array... it doesn't matter if you tap on the first grid item, second, third, etc... all grid items will segue to the ContactMainScreen(with the same data here)

Why doesn't it send the data for the specific contact in the foreach loop?


Solution

  • You need a @State for each cell so adding the navigationDestination code to the ContactCell can be one way of dong this.

    struct ContactCell: View{
        @Binding var contact: Contact
        @State var isPresenting: Bool = false
        var body: some View{
            Text(contact.name)
                .navigationDestination(isPresented: $isPresenting) {
                    ContactMainView(contact: $contact)
                }
                .onTapGesture {
                    isPresenting.toggle()
                }
        }
    }
    

    You can also use a ViewModifier

    struct ContactsListView: View {
        @State var contacts: [Contact] = (0...10).map { n in
            Contact(name: "\(n)")
        }
        var body: some View {
            LazyHGrid(rows: Array(repeating: .init(), count: 4)) {
                ForEach($contacts, id: \.id) { $contact in
                    // Creating a grid item for each contact
                    ContactCell(contact: contact)
                        .modifier(PresentViewModifier(contact: $contact))
                }
            }
        }
    }
    struct PresentViewModifier: ViewModifier{
        @State var isPresenting: Bool = false
        @Binding var contact: Contact
        func body(content: Content) -> some View {
            content
                .navigationDestination(isPresented: $isPresenting, destination: {
                    ContactMainView(contact: $contact)
                })
                .onTapGesture {
                    isPresenting.toggle()
                }
        }
    }