Why does ForEach not work with onTapGesture modifier correctly in SwiftUI

1.1k views Asked by At

I am trying to figure out why all the elements in a ForEach array change when only a single element is tapped.

import SwiftUI

struct SwiftUIView: View {
    
    @State var boolTest = false
    
    var nums = ["1","2","3","4","5","6","7"]
    
    var body: some View {
        VStack {
            ForEach(nums, id: \.self) { num in
                Text("\(num)")
                    .font(.system(size: 70))
                    .foregroundColor(boolTest ? .red : .green)
                    .onTapGesture {
                        boolTest.toggle()
                    }
            }
            
        }
    }
}

For example, when I tap on the view containing 1, all the other numbers also change colors. I want to be able to click on a number and have only that number change color.

1

There are 1 answers

0
HunterLion On BEST ANSWER

You are using one single variable for all elements of the array nums. So, the foreground color will always be the same - they all read the same value.

What you need is to create a subview that manages its own color independently. Separate the ForEach from its content, like this:

struct SwiftUIView: View {
    
    // This variable should not be here: all elements read the same value
    // @State var boolTest = false
    
    var nums = ["1","2","3","4","5","6","7"]
    
    var body: some View {
        VStack {
            ForEach(nums, id: \.self) { num in

                // Call another view
                Subview(text: num)
            }
            
        }
    }
}

// Create a subview with its own variable for each element
struct Subview: View {

    // Here, there is one boolTest for each element
    @State private var boolTest = false
    let text: String

    var body: some View {
                Text(text)
                    .font(.system(size: 70))
                    .foregroundColor(boolTest ? .red : .green)
                    .onTapGesture {
                        boolTest.toggle()
                    }
    }
}