Несогласованное поведение объекта SwiftUI observableобъекта в зависимости от источника сайта вызова

#swiftui #sprite-kit

Вопрос:

В продолжающемся стремлении передать данные из сцены SpriteKit в представление SwiftUI я обнаружил следующую загадку (по крайней мере, для меня). Я надеюсь, что это решение может вывести нас из тупика. У меня есть представление содержимого, которое использует SpriteView() для размещения/отображения сцены SpriteKit под названием GameScene. У меня есть класс под названием Counter(), который относится к подклассу ObservableObject. (Обратите внимание на инструкцию печати в теле функции добавления(подсчета).)

 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, с целью тестирования и сравнения, я добавил кнопку, которая вызывает функцию добавления(подсчета) :

 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)")
        }
    }
}
 

Когда нажимается кнопка (в представлении содержимого), количество увеличивается и отображается немедленно, как и ожидалось.

В GameScene у меня есть практически тот же вызов функции добавления(подсчета), но он завершается неудачей (отказывается?) чтобы обновить представление содержимого.

 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)
        }
    }
 }
 

Инструкция print читается одинаково независимо от того, поступает ли вызов из GameScene или contentView. При первом нажатии любой из кнопок на нем появляется надпись:

Добавить 1; новое общее количество: 1

Добавить 1; новое общее количество: 2

Добавьте 1; новое общее число: 3 и так далее.

Другими словами, вплоть до вызова функции, предназначенной для обновления опубликованного var, они, похоже, ведут себя одинаково. Но…

Тайна:

Почему вызов из contentView вызывает желаемое обновление, в то время как тот же вызов из GameScene этого не делает?

Я с нетерпением жду, когда с моих усталых глаз снимут чешую!

Ответ №1:

В вашем GameScene случае вы создаете совершенно новый экземпляр Counter , когда объявляете свойство:

 var counter = Counter()
 

Вместо этого вы должны передавать экземпляр Counter owned by ContentView to GameScene , чтобы они мутировали один и тот же объект.

Вы могли бы создать инициализатор GameScene , чтобы использовать a 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
 

Комментарии:

1. Я последовал предложению @jnpdx, которое также требовало изменить вызов в GameScene на счетчик?.добавить(количество: 1), и теперь это работает. Я подозревал, что проблема была в воссоздании экземпляра, и попытался использовать счетчик var: Счетчик? Но мне не хватало смены сцены в contentView. Большая благодарность за то, что нашли время помочь!