Ufuk Köşker
Ufuk Köşker

Reputation: 1480

Changing value of just clicked object in ForEach

When I click on the Cell at the top, I only want the text "Clicked" to appear on that object. Why do I see this text on other cells? How can I solve this problem?

enter image description here

CellView

struct CellView: View {
    @Binding var text: String
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 30)
                .foregroundColor(Color(UIColor.secondarySystemBackground))
                .frame(width: 300, height: 100)
            Text(text)
        }
    }
}

ContentView

struct ContentView: View {
    @State var text: String = ""
    var body: some View {
        VStack {
            ForEach(0 ..< 5) { item in
                
                Button(action: {
                    self.text = "Clicked"
                }) {
                    CellView(text: $text) 
                }
            }
        }
    }
}

Upvotes: 0

Views: 297

Answers (2)

Joe
Joe

Reputation: 3761

You are updating a single String text which is used for all cells. When it changes all the cells will be updated with that single string. Here are a couple options:

You can make the cell responsible for storing and updating it's own state:

struct CellView: View {
    @State var text: String = ""

    var body: some View {
        Button(action: {
            self.text = "Clicked"
        }) {
            ZStack {
                RoundedRectangle(cornerRadius: 30)
                    .foregroundColor(Color(UIColor.secondarySystemBackground))
                    .frame(width: 300, height: 100)
                Text(text)
            }
        }
    }
}

struct CellClickContentView: View {
    var body: some View {
        VStack {
            ForEach(0 ..< 5) { item in
                CellView()
            }
        }
    }
}

or you could somehow store the state for each cell in the parent view:

class CellState: ObservableObject, Identifiable {
    var id = UUID()
    @Published var text = ""
}

struct CellView: View {
    @ObservedObject var state: CellState

    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 30)
                .foregroundColor(Color(UIColor.secondarySystemBackground))
                .frame(width: 300, height: 100)
            Text(state.text)
        }
    }
}

struct CellClickContentView: View {
    @State var states: [CellState] = (0..<5).map { _ in CellState() }

    var body: some View {
        VStack {
            ForEach(states) { state in
                Button(action: {
                    state.text = "Clicked"
                }) {
                    CellView(state: state)
                }
            }
        }
    }
}

Upvotes: 1

You see the text on other cells because this is what you display in all the cells. Try something like this:

struct ContentView: View {
    @State var text: String = ""
    @State var selection: Int?
    
    var body: some View {
        VStack {
            ForEach(0 ..< 5) { item in
                Button(action: {
                    selection = item
                    text = "Clicked"
                }) {
                    if selection == item {
                        CellView(text: $text)
                    } else {
                        CellView(text: .constant(""))
                    }
                }
            }
        }
    }
}

Upvotes: 1

Related Questions