I'm trying to update the colour of an SKEmitterNode within a UIViewRepresentable. The hue value is passed in from the state on the parent View, and the emitter colour should update when the hue value in the parent state updates.
It initially displays, and although it updates on the first call to updateUIView it does not respond to any subsequent calls, even though the function definitely gets called with the new value of hue each time.
Has anyone any idea why the emitter won't update? I'm at the hair-tearing-out stage...
import SwiftUI
import UIKit
import SpriteKit
struct EmitterView: UIViewRepresentable {
private let view = SKView()
let scene = SKScene(fileNamed: "myScene")!
var emitter: SKEmitterNode = SKEmitterNode()
var hue: Double
func makeUIView(context: UIViewRepresentableContext<EmitterView>) -> SKView {
// Lets make it manually
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleBirthRate = 80
emitter.particleLifetime = 2.5
emitter.particlePositionRange = CGVector(dx: 200, dy: 150)
emitter.particleScale = 0.2
emitter.particleScaleSpeed = 0.45
emitter.particleColor = SKColor.blue
emitter.particleColorBlendFactor = 1.0
scene.addChild(emitter)
view.presentScene(scene)
return view
}
func updateUIView(_ uiView: SKView, context: UIViewRepresentableContext<EmitterView>) {
let color: SKColor = SKColor(
hue: CGFloat(hue),
saturation: 1.0,
brightness: 1.0,
alpha: 1.0
)
print("Hue is now", hue)
emitter.resetSimulation()
emitter.particleColor = color
}
}
struct EmitterView_Previews: PreviewProvider {
static var previews: some View {
EmitterView(hue: 0.5)
}
}
Now that 11 is out I can finally play with Swift UI.
The problem being experienced is that your
UIViewRepresentableis a struct.The way structs work is they are copy on write.
I am going to assume that in your scene delegate, you are not reassigning the struct in your root controller when you set the hue.
You need to move your code into class for best results (Not the coordinator, they are used for application flow. Research Coordinator Pattern for more info.)
The best class would be your SKScene.
I recommend making a GameScene class and changing your sks file to point to it.
You can then create a function to this game scene class that will allow you to alter the emitter without altering the struct.
The reason why this works is because on copy, the
viewwill copy the pointer, where ashuewill copy the value.If you need to do things in the updateUIView, you can always cast uiView.scene to GameScene and access it that way.