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

@ObservableObject已更新但View Body未刷新问题求助

问题分析与解决方案

核心问题拆解

  1. MoneyMaker 未实现可观察机制:你在 MoneyMakerCell 中用 @ObservedObject 标记 moneyMaker,但 MoneyMaker 本身没有遵循 ObservableObject,也未用 @Published 标记需要监听的属性,导致属性变化无法触发视图刷新。
  2. MoneyMakerCell 冗余 @State 变量:单元格内的 name、manager 等 @State 变量是独立状态,没有与传入的 moneyMaker 属性绑定,即使 moneyMaker 数据更新,这些变量也不会同步变化,视图自然不会更新。
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:37:07