SwiftUI传递@State至方法后onTapGesture失效问题求助
问题
我需要根据行列数绘制网格(比如2行2列生成4个单元格),每个单元格绑定.onTapGesture,点击时更改颜色。我知道要用@State var跟踪颜色变化来触发重绘,但把@State var cellColors传入获取颜色的方法后,.onTapGesture完全不触发,这是为什么?
移除@State var cellColors时,partColor方法能正常调用,但我需要这个状态变量实现颜色变化后的自动重绘,请问我忽略了什么细节?
相关代码
View代码
struct EditGridView: View { @ObservedObject var playViewModel: PlayViewModel @State var cellColors: [Color] var body: some View { VStack { ForEach(0..<playViewModel.playViewState.currentInstrumentsSet.rows, id: \.self) { row in HStack { ForEach(0..<playViewModel.playViewState.currentInstrumentsSet.columns, id: \.self) { column in ZStack { RoundedRectangle(cornerRadius: 7.0) .fill( playViewModel.partColor( row: row, column: column, cellColors: cellColors ) ) .overlay(RoundedRectangle(cornerRadius: 7.0).stroke(Color("GridBorderColor"))) .cornerRadius(7.0) } .onTapGesture { cellColors = playViewModel.tapOnCell( index: InstrumentsSet.Track.Part.Index(row: row, column: column), currentCellColors: cellColors ) } } } } }.aspectRatio(1.77777, contentMode: .fit) } }
ViewModel中的partColor方法
public func partColor(row: Int, column: Int, cellColors: [Color]) -> Color { let index: Int = row * setSettings.gridColumns + column let color = cellColors[index] print("color \(color) at index: \(index)") return color }
解答
核心问题:@State的使用场景错误
@State是SwiftUI专门用来管理View内部私有状态的属性包装器,它的生命周期由SwiftUI完全托管,不能通过外部初始化参数直接传入。你现在把cellColors声明为@State var同时作为外部传入属性,会导致SwiftUI无法正确跟踪状态变化,甚至破坏View的更新逻辑,最终引发手势无法触发的问题。
解决思路与具体修改
方案:将状态移至ViewModel(推荐)
把cellColors放到PlayViewModel中,用@Published修饰,让ViewModel统一管理状态逻辑,同时SwiftUI能自动监听变化并更新View。
- 修改PlayViewModel
class PlayViewModel: ObservableObject { // 保留原有代码,新增@Published状态 @Published var cellColors: [Color] // 初始化时传入初始颜色数组 init(/* 原有初始化参数 */, cellColors: [Color]) { self.cellColors = cellColors // 其他初始化逻辑 } // 简化partColor方法,无需再传cellColors参数 public func partColor(row: Int, column: Int) -> Color { let index: Int = row * setSettings.gridColumns + column let color = cellColors[index] print("color \(color) at index: \(index)") return color } // 修改tapOnCell方法,直接修改ViewModel内的状态 public func tapOnCell(index: InstrumentsSet.Track.Part.Index) { let cellIndex = index.row * setSettings.gridColumns + index.column // 这里替换成你的颜色切换逻辑,示例: cellColors[cellIndex] = cellColors[cellIndex] == .gray ? .orange : .gray } }
- 修改EditGridView
struct EditGridView: View { @ObservedObject var playViewModel: PlayViewModel // 移除原有的@State var cellColors var body: some View { VStack { ForEach(0..<playViewModel.playViewState.currentInstrumentsSet.rows, id: \.self) { row in HStack { ForEach(0..<playViewModel.playViewState.currentInstrumentsSet.columns, id: \.self) { column in ZStack { RoundedRectangle(cornerRadius: 7.0) .fill(playViewModel.partColor(row: row, column: column)) .overlay(RoundedRectangle(cornerRadius: 7.0).stroke(Color("GridBorderColor"))) .cornerRadius(7.0) } .onTapGesture { playViewModel.tapOnCell(index: InstrumentsSet.Track.Part.Index(row: row, column: column)) } } } } }.aspectRatio(1.77777, contentMode: .fit) } }
为什么这样能解决问题?
@Published修饰的cellColors会在值变化时自动发送通知,SwiftUI通过@ObservedObject监听ViewModel,一旦状态更新就会触发View重绘。- 手势触发时直接调用ViewModel的方法修改状态,避免了
@State外部传入导致的生命周期混乱,手势能正常响应。
内容的提问来源于stack exchange,提问作者Symphonic eMotions
相关产品推荐
相关产品推荐

