ForEach view can't apply the view modifier to selected view

Viewed 99

I have a simple horizontal list view that displays the list of colours and the problem is that I don't know how to apply border to only one selected view, for now it's adding the white border to all views in the list on didTap, please help if you know how to achieve it, thanks

Here's the code:

struct Colour: Identifiable {
    var id: Int
    var color: Color
}

struct ContentView: View {
    @State private var didTap = false
    @State private var colors: [Colour] = [
        Colour(id: 1, color: .black),
        Colour(id: 2, color: .yellow),
        Colour(id: 3, color: .orange),
        Colour(id: 4, color: .green),
        Colour(id: 5, color: .red),
        Colour(id: 6, color: .blue),
        Colour(id: 7, color: .pink),
        Colour(id: 8, color: .purple),
    ]
    
    var body: some View {
        ZStack {
            Color.gray.opacity(0.2)
                .ignoresSafeArea()
            
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 4) {
                    ForEach(colors) { color in
                        Rectangle()
                            .fill(color.color)
                            .frame(width: 100, height: 150)
                            .overlay(
                                Rectangle()
                                    .stroke(didTap ? Color.white : .clear, lineWidth: 5)
                                    .animation(.spring())
                            )
                            .padding(5)
                            .onTapGesture {
                                didTap.toggle()
                                print(color.id)
                            }
                    }
                }
                .padding(.leading, 8)
            }
        }
    }
}
1 Answers

You need to store id of selected color instead of bool.

Here is simple demo (deselect on tap on same element is your excise). Tested with Xcode 12.5 / iOS 14.5

demo

struct ContentView: View {
    @State private var selected = -1          // << here !!
    @State private var colors: [Colour] = [
        Colour(id: 1, color: .black),
        Colour(id: 2, color: .yellow),
        Colour(id: 3, color: .orange),
        Colour(id: 4, color: .green),
        Colour(id: 5, color: .red),
        Colour(id: 6, color: .blue),
        Colour(id: 7, color: .pink),
        Colour(id: 8, color: .purple),
    ]

    var body: some View {
        ZStack {
            Color.gray.opacity(0.2)
                .ignoresSafeArea()

            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 4) {
                    ForEach(colors) { color in
                        Rectangle()
                            .fill(color.color)
                            .frame(width: 100, height: 150)
                            .overlay(
                                Rectangle()
                                    .stroke(selected == color.id ? Color.white : .clear, lineWidth: 5)
                                    .animation(.spring())
                            )
                            .padding(5)
                            .onTapGesture {
                                selected = color.id    // << here !!
                                print(color.id)
                            }
                    }
                }
                .padding(.leading, 8)
            }
        }
    }
}
Related