You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,会导致点击不存在的卡片时误翻转第一张卡,属于逻辑漏洞。
修复方案
  1. 检查MemoryGame的嵌套Card类型定义,将class Card改为struct Card,确保其为值类型,保留Identifiable协议遵守,保证id属性是初始化后不会变更的唯一标识,isFaceUp初始值设为false(符合教程初始卡片背面朝上的逻辑)。
  2. 替换存在逻辑漏洞的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
}
  1. 补全MemoryGame的初始化方法,确保创建卡片时正确传入id、内容,isFaceUp、isMatched默认值为false。

修复完成后点击卡片,@Published会正常检测到模型值变更,驱动视图同步刷新,效果和教程演示一致。

内容的提问来源于stack exchange,提问作者doesntpost

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 06:45:31