我试图将游戏数据从主视图传递到ForEach中的子单元格。这看起来是可行的,可观察对象正在更新,但视图本身似乎没有更新。下面是我们创建单元格的主GameView
struct GameView: View {
@State var money: Float! = 0.0
@ObservedObject var moneyMakers: MoneyMakerModel
@State var companyName: String! = "Alpaca Inc."
@State var gameMoney: Float = SavedGame.shared.money
@Environment(\.defaultMinListRowHeight) var minRowHeight
init() {
self.moneyMakers = MoneyMakerModel.shared
GameManager.GAME_VIEW = self
}
var body: some View {
ScrollView {
HStack {
Text("$\(gameMoney)")
.frame(maxWidth: .infinity)
Text("Alpaca Inc.")
.frame(maxWidth: .infinity)
.font(Font.headline)
Image(systemName: "gearshape")
.frame(maxWidth: .infinity, alignment: .trailing)
Spacer()
}
Spacer()
VStack {
ForEach(moneyMakers.moneyMakers) { moneyMaker in
MoneyMakerCell(moneyMaker: moneyMaker)
.frame(maxWidth: .infinity)
.padding([.top, .leading, .trailing])
}
.frame(minHeight: minRowHeight * CGFloat(STARTING_MONEY_MAKERS.count))
}
}
}
}
我正在使用一个模型来传递一个Observable对象,如下所示
class MoneyMakerModel: ObservableObject {
@Published var moneyMakers: \[MoneyMaker\] = SavedGame.shared.moneyMakers
public static let shared = MoneyMakerModel()
private init(){}
}
在我的牢房里,我就像这样吃这个。如果我在下面的“@observedObject”处的代码中放置一个断点,我可以看到它在MoneyMakerModel中的数据更新时被调用,但仍然没有在MoneyMakerCellView的主体上运行。
import SwiftUI
struct MoneyMakerCell: View {
@State var name: String! = "Placeholder"
@State var manager: Bool! = false
@State var incomeManagerRate: Float! = 0.0
@State var level: Int! = 0
@State var updateLevel: Int! = 0
@State var maxUpdateLevel: Int! = 0
@ObservedObject var moneyMaker: MoneyMaker // <--- This runs on breakpoint but no update below
var body: some View {
ZStack {
Color.yellow.opacity(0.4)
VStack {
Spacer()
HStack {
Spacer()
Text(name)
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)
Button("Upgrade") {
do {
try GameManager.upgradeMoneyMaker(moneyMaker: moneyMaker)
} catch (let error) {
print("ERROR: \(error)")
// TODO: SHOW ERROR MESSAGE
}
}
.frame(maxWidth: .infinity, alignment: .trailing)
Spacer()
}
Spacer()
ProgressView("Upgrade \(self.moneyMaker.updateLevel)/\(moneyMaker.maxUpdateLevel)", value: (Float(self.moneyMaker.updateLevel)), total: 100)
.padding([.leading, .trailing])
Spacer()
if !manager {
HStack {
Spacer()
Text("$\(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()
}
}
}
}
我真的很想得到一些帮助,因为过去两天我一直在为它疯狂。我想我误解了观测对象是如何工作的,我很想理解并克服这个障碍。
我尝试切换到StateObject和@State,但没有成功
2条答案
按热度按时间1wnzp6jl1#
我认为问题出在MoneyMakerCell上。我将提供一些基本的代码,希望你能解决你的问题。这就是我创建可观察对象的方式。
这是我初始化我的Observable Object并将moneyMakerModel作为环境对象传递到我的MoneyMakerCell的地方。
这就是我的MoneyMakerCell的样子。
这是一个简单的例子,在您使用自定义对象的情况下,仅使用字符串。复制并粘贴到Xcode中,看看它是如何工作的,希望这能帮助你解决你的问题:)
tkclm6bt2#
一般
您的解决方案有几个问题。但首先,我建议阅读和工作,通过介绍教程。具体了解@State、
@ObservedObject
和@StateObject
的区别和用例。另外,了解ObservableObject的已发布属性的关键约束是什么。为什么你看不到变化
因此,代码中的主要问题是,您正在使用一个ObservableObject,其发布的属性具有引用语义。这意味着,ObservableObject仅在 * 引用值 * 将更改时发送更改事件-而不是 * 当该引用的任何属性将更改时 *。虽然有解决方法,但通常建议保持简单,并为您发布的属性专门使用具有 * 值语义 * 的类型(Swift结构,原始类型等)。
所以,上面的这就是为什么你在视图中没有收到更改通知的原因--没有。
绑定模型到视图
另一个问题是如何提供模型。基本上,当任何视图本身正在创建模型时,您应该使用
@StateObject
属性 Package 器来声明模型的生命周期绑定到 * 底层 * 视图(而不是SwiftUI视图!)的生命周期。注意,在这种情况下,视图 * 拥有 * 模型,没有人可以看到模型(但可能会看到副作用)。模型是视图的私有模型。当视图模型在视图之外定义并且与视图具有不同的生存期时,您应该使用
@ObservedObject
属性 Package 器将模型绑定到视图。惯用的方法是让父视图在子视图中设置模型。在你的代码中,你使用视图的初始化器来设置模型。什么是UIView?
在这里,我担心有一个误解什么是SwiftUI的视图初始化器,什么是SwiftUI视图实际上是所有关于。不是你想的那样。SwiftUI是一个声明式UI框架。将SwiftUI View视为一个 * 函数 *,您可以在其中创建和修改底层的实际视图(渲染像素)。因此,SwiftUI视图是组合这些创建和突变函数的一种方法。SwiftUI视图的初始化器可能会设置create/modifier函数的那些参数(实际上,SwiftUI实现稍微复杂一些,它使用的闭包实际上只会在底层视图的初始化过程中调用一次)。
在你的代码中意味着什么:每当你对你的视图进行更改时,你就创建了这个SwiftUI视图,模型将被设置-每次都是如此。实际上,您只需要在创建底层视图(使用相同的SwiftUI视图)时设置它 * 一次 *。所以,即使你的代码看起来没有什么害处,你也应该避免使用令人困惑的代码,而采用惯用的方法。
嗯...
这份声明
这绝对是一个信号,你需要了解SwiftUI视图实际上是什么。我敢打赌,你试图完成的事情至少是非常麻烦的,可能不会起作用,或者有你还没有意识到的不良副作用。
”””好吧,接下来该怎么办?**
我建议从重构你的“应用状态”开始。请注意,AppState不是一个“Model”,它只是没有逻辑的普通数据。您的Model将添加逻辑并可能在内部修改应用状态。可以肯定的是,应该只有一个模型可以更改您的应用程序状态。您的逻辑将由模型上调用的事件和方法引起。然后模型发布新的已发布值。
通过使用具有值语义的类型(结构体、枚举)来定义您的应用程序状态。只是不要为App State使用类。
另外,不要在可变状态中使用共享全局变量!切勿腹主动脉瘤腔内修复术!
接下来,使用惯用的方法来定义SwiftUI视图,使用
@StateObject
或@ObservableObject
,其中绑定使用值语义发布值的模型。不要使用SwiftUI Environment来传递视图状态,而是在SwiftUI View层次结构的某个根视图中,提取已发布的属性并将其传递给子视图。这样,子视图就不需要从模型中知道任何东西--它们只需要得到字符串、数字等。他们所呈现的。
我建议将环境用于依赖项和配置,而不是用于传递模型对象。这是可能的,但从设计的Angular 来看,它会导致不希望的耦合。
我想,这是很多事情。现在,轮到你了,编码快乐!