CS193 SwiftUI MVVM架构下视图未随模型状态更新问题
问题根因
- 控制台两次打印
card.isFaceUp结果一致是正常现象,不是逻辑未执行的依据:onTapGesture闭包里捕获的card是ForEach遍历时生成的临时不可变值拷贝,后续ViewModel内的数据修改不会影响这个局部常量的值,不要被这个打印结果误导。 - 视图不刷新的核心原因:你大概率将
MemoryGame内的Card类型定义成了class(引用类型),没有按照教程要求定义为struct(值类型)。@Published触发视图更新的前提是被修饰的属性本身发生值变更。你用@Published修饰的model是值类型MemoryGame<String>,如果Card是引用类型,cards数组存储的只是指向Card实例的指针:修改Card实例的isFaceUp属性时,数组内存储的指针地址没有变化,对MemoryGame结构体来说,自身所有存储属性的值都没有发生改变,@Published无法检测到内部引用类型的属性变更,自然不会发送通知驱动视图刷新。
如果Card是值类型,修改数组内元素的属性会触发数组的写时复制机制,整个MemoryGame结构体的值会同步更新,@Published就能正常触发视图刷新。 - 额外隐患:你写的
index(of:)方法在找不到对应卡片时会默认返回0,会导致点击不存在的卡片时误翻转第一张卡,属于逻辑漏洞。
修复方案
- 检查
MemoryGame的嵌套Card类型定义,将class Card改为struct Card,确保其为值类型,保留Identifiable协议遵守,保证id属性是初始化后不会变更的唯一标识,isFaceUp初始值设为false(符合教程初始卡片背面朝上的逻辑)。 - 替换存在逻辑漏洞的
index(of:)方法,移除无效的兜底返回,找不到卡片时直接抛出错误避免静默逻辑异常:
func index(of card: Card) -> Int { guard let matchedIndex = cards.firstIndex(where: { $0.id == card.id }) else { fatalError("Find card failed: card \(card) not exist in cards array") } return matchedIndex }
- 补全
MemoryGame的初始化方法,确保创建卡片时正确传入id、内容,isFaceUp、isMatched默认值为false。
修复完成后点击卡片,@Published会正常检测到模型值变更,驱动视图同步刷新,效果和教程演示一致。
内容的提问来源于stack exchange,提问作者doesntpost
相关产品推荐
相关产品推荐

