SwiftUI ObservableObject 的行为不一致,具体取决于调用站点的来源

Posted

技术标签:

【中文标题】SwiftUI ObservableObject 的行为不一致,具体取决于调用站点的来源【英文标题】:Inconsistent behavior of SwiftUI ObservableObject depending on source of call site 【发布时间】:2021-06-14 14:44:22 【问题描述】:

在将数据从 SpriteKit 场景传递到 SwiftUI 视图的持续探索中,我发现了以下谜团(至少对我而言)。我希望解决方案可以打破僵局。 我有一个 ContentView,它使用 SpriteView() 来包含/显示一个名为 GameScene 的 SpriteKit 场景。 我有一个名为 Counter() 的类,它是 ObservableObject 的子类。 (注意 add(count) 函数体中的 print 语句。)

import SwiftUI

class Counter: ObservableObject 
@Published var count : Int = 0 

func add(count: Int) 
    self.count += count
    print("Add \(count); new total: \(self.count)")
 

在 ContentView 中,为了测试和比较,我添加了一个按钮,调用 add(count) 函数:

import SwiftUI
import SpriteKit

struct ContentView: View 
    @ObservedObject var counter = Counter()
    var scene: SKScene 
        let scene = GameScene()
            scene.size = CGSize(width: 300, height: 400)
            scene.scaleMode = .fill
            return scene
          
    var body: some View 
        VStack
            SpriteView(scene: scene)
                .frame(width: 300, height: 400)
                .ignoresSafeArea()
            
            Button
                counter.add(count: 1)
             label: 
                Text("Add to count")
            
            Text("New count = \(counter.count)")
        
    

当按钮(在 ContentView 中)被点击时,计数会增加并立即按预期显示。

在 GameScene 中,我对 add(count) 函数的调用几乎相同,但更新 ContentView 失败(拒绝?)。

class GameScene: SKScene 
var counter = Counter()
var count = 0
...
//a SpriteKitNode called "button" is created then added in didMove(toView)//
...
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) 
        guard let touch = touches.first else  return 
        let location = touch.location(in: self)
        if button.contains(location) 
            counter.add(count: 1)
        
    
 

无论调用来自 GameScene 还是 ContentView,print 语句的内容都是相同的。第一次点击任一按钮时,它会显示:

加1;新总数:1

加1;新总数:2

加1;新总数: 3 ,依此类推。

换句话说,在调用用于更新已发布变量的 func 之前,它们似乎表现相同。但是……

谜团:

为什么来自 ContentView 的调用会触发所需的更新,而来自 GameScene 的相同调用却不会?

我期待着从我疲惫的眼睛中去除鳞片!

【问题讨论】:

【参考方案1】:

在您的GameScene 中,当您声明属性时,您正在创建一个全新的Counter 实例:

var counter = Counter()

相反,您应该将ContentView 拥有的Counter 实例传递给GameScene,以便它们对同一个对象进行变异。

您可以为GameScene 创建一个初始化程序以将Counter 作为参数,或者您可以执行以下操作:

//in GameScene:
var counter : Counter?
//in GameScene when the button is pressed:
counter?.add(count: 1)

//in ContentView:
let scene = GameScene()
scene.counter = counter

【讨论】:

我遵循了@jnpdx 的建议,它还需要将 GameScene 中的调用更改为 counter?.add(count: 1),现在它可以工作了。我怀疑问题出在实例的重新创建上,并尝试使用 var counter: Counter?但我错过了 ContentView 中对场景的更改。非常感谢您花时间提供帮助!

以上是关于SwiftUI ObservableObject 的行为不一致,具体取决于调用站点的来源的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI/组合监听 ObservableObject 中的多个发布者

SwiftUI = ObservableObject 作为类的选择

SwiftUI 致命错误:未找到“”类型的 ObservableObject

在 SwiftUI 中将 ObservableObject 链接到 DatePicker

SwiftUI - 在 ObservableObject 类/依赖注入中使用 EnvironmentObject

SwiftUI:ObservableObject 在重绘时不会保持其状态