I am trying to use the isPaused:
argument in the SpriteView
initializer to pause the SKScene
when a state property changes in SwiftUI.
@State private var showingLevelChooser = false
var body: some View {
SpriteView(scene: scene, isPaused: showingLevelChooser)
}
But when I use the state variable like:
SpriteView(scene: scene, isPaused: showingLevelChooser)
instead of just:
SpriteView(scene: scene)
the SKScene is recreated every time the variable changes, which is not what I want. I only want to pause the game.
As far as I understand this happens because SwiftUI recreates the views that are dependent on the state.
But if this is the case, how can you pause the SpriteKit scene from SwiftUI, without reinitializing it every time?
I created a sample Xcode 13 project here: https://github.com/clns/SpriteView-isPaused
The SKScene
is displaying the time elapsed on the screen, in seconds. Every time the SwiftUI sheet is presented and the state variable changes, the timer starts from 0 (zero), because the scene is recreated.
A preview is included in the GitHub project. I cannot upload an animated gif here.
You should better include the code as text, which helps involving more readers.
In your code, scene
is a computed property, so it is evaluated at each time body
is evaluated.
Generally, you should better avoid creating a computed property which creates a new instance, especially when the identity is important.
Please try something like this:
class GameScene: SKScene, ObservableObject { //<-
private let label = SKLabelNode(text: "Time Elapsed:\n0")
private var lastUpdateTime : TimeInterval = 0
override func didMove(to view: SKView) {
addChild(label)
}
override func update(_ currentTime: TimeInterval) {
if (self.lastUpdateTime == 0) {
self.lastUpdateTime = currentTime
}
let seconds = Int(currentTime - lastUpdateTime)
label.text = "Time Elapsed:\n\(seconds)"
label.numberOfLines = 2
}
}
struct ContentView: View {
@State private var showingLevelChooser = false
//↓
@StateObject var scene: GameScene = {
let scene = GameScene()
scene.size = CGSize(width: 300, height: 400)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.scaleMode = .fill
return scene
}()
var body: some View {
ZStack {
SpriteView(scene: scene, isPaused: showingLevelChooser)
.ignoresSafeArea()
VStack {
Button("Level Chooser") {
showingLevelChooser.toggle()
}
Spacer()
}
}
.sheet(isPresented: $showingLevelChooser) {
VStack {
Button("Cancel") {
showingLevelChooser.toggle()
}
Text("Level Chooser")
}
}
}
}