I'm trying to change the color of a button when it's pressed. It works as intended in the preview of WordleRowView.swift but when I press a button in WordleView.swift, every button in that row changes color.
WordleView.swift
import SwiftUI
class WordleRow: ObservableObject, Identifiable {
let id = UUID()
@Published var word: String
@Published var colors: [Color] = [.gray, .gray, .gray, .yellow, .green]
init(_ word: String) {
self.word = word
}
}
class WordleBoard: ObservableObject {
@Published var rows = [WordleRow]()
init() {
self.rows = [
WordleRow("hello"),
WordleRow("horse")
]
}
}
struct WordleView: View {
@ObservedObject var board = WordleBoard()
var body: some View {
NavigationView {
VStack {
List {
ForEach(board.rows) { wordleRow in
WordleRowView(wordleRow: wordleRow)
}
.onDelete { board.rows.remove(atOffsets: $0) }
.listRowBackground(Color.clear)
}
Button("Solve") {
print(board.rows[0].colors)
}
}
}
}
}
#Preview {
WordleView()
}
WordleRowView.swift
import SwiftUI
struct WordleRowView: View {
@ObservedObject var wordleRow: WordleRow
var body: some View {
HStack {
LetterBox(wr: wordleRow, idx: 0)
LetterBox(wr: wordleRow, idx: 1)
LetterBox(wr: wordleRow, idx: 2)
LetterBox(wr: wordleRow, idx: 3)
LetterBox(wr: wordleRow, idx: 4)
}
.padding(.horizontal)
.frame(maxWidth: .infinity)
.aspectRatio(contentMode: .fit)
}
}
#Preview {
WordleRowView(wordleRow: WordleRow("hello"))
}
struct LetterBox: View {
@ObservedObject var wr: WordleRow
let idx: Int
var letter: String {
String(wr.word[wr.word.index(wr.word.startIndex, offsetBy: idx)])
}
var body: some View {
Button(letter) {
switch wr.colors[idx] {
case .gray:
wr.colors[idx] = .yellow
case .yellow:
wr.colors[idx] = .green
default:
wr.colors[idx] = .gray
}
print(idx)
print(wr.colors)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.aspectRatio(1, contentMode: .fit)
.foregroundStyle(.white)
.background(wr.colors[idx])
}
}
I've tried using a dictionary instead of an array to store colors but the same problem occurs.
You can see from the console logs that the action for every button is firing when you use the WordleView
preview, but not when you use the WordleRowView
preview. The difference between these two is that you have a List
in WordleView
, and multiple buttons in a list row for some reason don't work properly unless you apply a style (see Buttons in SwiftUI List ForEach view trigger even when not "tapped"? for another example).
Applying .buttonStyle(.plain)
in LetterBox
fixes your issue.