@ObservableObject已更新但View Body未刷新问题求助
问题分析与解决方案
核心问题拆解
MoneyMaker未实现可观察机制:你在MoneyMakerCell中用@ObservedObject标记moneyMaker,但MoneyMaker本身没有遵循ObservableObject,也未用@Published标记需要监听的属性,导致属性变化无法触发视图刷新。MoneyMakerCell冗余@State变量:单元格内的name、manager等@State变量是独立状态,没有与传入的moneyMaker属性绑定,即使moneyMaker数据更新,这些变量也不会同步变化,视图自然不会更新。GameView中gameMoney未同步更新:gameMoney是@State变量,仅初始化时从SavedGame取值,后续SavedGame数据变化无法自动同步到该变量。
分步修复代码
1. 改造 MoneyMaker 类,实现可观察
让 MoneyMaker 遵循 ObservableObject 和 Identifiable(满足 ForEach 遍历要求),并用 @Published 标记需要监听的属性:
class MoneyMaker: ObservableObject, Identifiable { let id = UUID() // 唯一标识,满足Identifiable要求 @Published var name: String @Published var manager: Bool @Published var incomeManagerRate: Float @Published var level: Int @Published var updateLevel: Int @Published var maxUpdateLevel: Int // 根据你的业务逻辑补充初始化方法 init(name: String, manager: Bool, incomeManagerRate: Float, level: Int, updateLevel: Int, maxUpdateLevel: Int) { self.name = name self.manager = manager self.incomeManagerRate = incomeManagerRate self.level = level self.updateLevel = updateLevel self.maxUpdateLevel = maxUpdateLevel } }
2. 重构 MoneyMakerCell,移除冗余状态
直接使用传入的 moneyMaker 属性,去掉独立的 @State 变量,依赖 @ObservedObject 监听数据变化:
import SwiftUI struct MoneyMakerCell: View { @ObservedObject var moneyMaker: MoneyMaker var body: some View { ZStack { Color.yellow.opacity(0.4) VStack { Spacer() HStack { Spacer() Text(moneyMaker.name) .font(.headline) .frame(maxWidth: .infinity, alignment: .leading) Button("Upgrade") { do { try GameManager.upgradeMoneyMaker(moneyMaker: moneyMaker) } catch let error { print("ERROR: \(error)") } } .frame(maxWidth: .infinity, alignment: .trailing) Spacer() } Spacer() ProgressView( "Upgrade \(moneyMaker.updateLevel)/\(moneyMaker.maxUpdateLevel)", value: Float(moneyMaker.updateLevel), total: Float(moneyMaker.maxUpdateLevel) ) .padding([.leading, .trailing]) Spacer() if !moneyMaker.manager { HStack { Spacer() Text("$\(moneyMaker.incomeManagerRate)") .frame(maxWidth: .infinity, alignment: .leading) Button("Hire Manager") { do { try GameManager.upgradeMoneyMaker(moneyMaker: moneyMaker) } catch let error { print("ERROR: \(error.localizedDescription)") } } .frame(maxWidth: .infinity, alignment: .trailing) Spacer() } } Spacer() } } } }
3. 修复 GameView 的状态同步问题
让 SavedGame 也实现 ObservableObject,并直接监听它的 money 属性,同时移除全局引用的不良写法:
// 先改造SavedGame class SavedGame: ObservableObject { static let shared = SavedGame() private init() {} @Published var money: Float = 0.0 @Published var moneyMakers: [MoneyMaker] = [] // 初始化你的默认数据 } // 再修改GameView struct GameView: View { @State var companyName: String = "Alpaca Inc." @ObservedObject var moneyMakersModel: MoneyMakerModel @ObservedObject var savedGame: SavedGame = SavedGame.shared @Environment(\.defaultMinListRowHeight) var minRowHeight init() { self.moneyMakersModel = MoneyMakerModel.shared // 移除 GameManager.GAME_VIEW = self,改用ObservableObject传递状态 } var body: some View { ScrollView { HStack { Text("$\(savedGame.money)") .frame(maxWidth: .infinity) Text(companyName) .frame(maxWidth: .infinity) .font(Font.headline) Image(systemName: "gearshape") .frame(maxWidth: .infinity, alignment: .trailing) Spacer() } Spacer() VStack { ForEach(moneyMakersModel.moneyMakers) { moneyMaker in MoneyMakerCell(moneyMaker: moneyMaker) .frame(maxWidth: .infinity) .padding([.top, .leading, .trailing]) } .frame(minHeight: minRowHeight * CGFloat(moneyMakersModel.moneyMakers.count)) } } } }
关键原理说明
ObservableObject+@Published:只有当数据类遵循ObservableObject,且属性用@Published标记时,SwiftUI 才能感知到属性变化并触发视图刷新。- 避免冗余
@State:子视图应直接依赖传入的可观察对象属性,而非维护独立的@State,否则会导致数据不同步。 - 状态管理范式:尽量避免全局引用(如
GameManager.GAME_VIEW = self),改用ObservableObject传递和监听状态,符合 SwiftUI 的响应式设计理念。
内容的提问来源于stack exchange,提问作者Charlie Oxendine
相关产品推荐
相关产品推荐

