为何SwiftUI中数据遵循Equatable时ForEach无法触发视图更新
问题原因
核心问题出在你自定义的Equatable实现逻辑不完整,误导了SwiftUI的视图差分机制,导致框架判定卡片数据没有变化,跳过了视图刷新。
- SwiftUI更新
ForEach渲染的网格/列表时,会对遵循Equatable的列表元素做等值校验:如果新旧位置上的元素判定为相等,就直接复用已渲染的旧子视图,不会用新数据重绘;只有判定元素不相等时,才会刷新对应子视图。 - 你实现的
==方法只对比了Card的id属性,完全没有校验isFaceUp、isMatched、content这些直接决定UI展示效果的属性。 - 点击卡片调用
choose方法修改isFaceUp时,新旧版本的卡片id完全一致,按照你写的相等规则,SwiftUI会认为这张卡片没有任何改动,自然不会更新对应的CardView。
至于移除Equatable遵循后视图恢复正常,原因也很简单:Card本身是结构体,当你不手动实现Equatable时,SwiftUI做差分校验会自动对比它的所有存储属性,能正确识别到isFaceUp的改动,就会正常触发视图刷新。
修复方案
两种方案选一个即可:
- 补全
Equatable的判断逻辑,把所有影响UI展示的属性都加入对比:
static func == (lhs: MemoryGame<CardContent>.Card, rhs: MemoryGame<CardContent>.Card) -> Bool { lhs.id == rhs.id && lhs.isFaceUp == rhs.isFaceUp && lhs.isMatched == rhs.isMatched && lhs.content == rhs.content }
- 如果你没有特殊的等值判断需求,直接删掉手动写的
Equatable遵循声明和自定义==方法,让系统自动为结构体合成Equatable逻辑即可——系统自动生成的==默认会对比所有存储属性,不会出现属性漏判的问题。
额外说明:你当前
choose方法里用传入的card参数找索引的逻辑,在仅对比id的Equatable实现下是能正常工作的,问题完全出在视图更新的差分环节,和索引查找逻辑无关。
内容的提问来源于stack exchange,提问作者Raahs
相关产品推荐
相关产品推荐

