问题
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)
}
}
回答1:
Now that 11 is out I can finally play with Swift UI.
The problem being experienced is that your UIViewRepresentable
is 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.
import SwiftUI
import UIKit
import SpriteKit
struct EmitterView: UIViewRepresentable {
private let view = SKView()
let scene = GameScene(fileNamed: "myScene")!
var hue : Double{
get{
return scene.hue
}
set{
scene.hue = newValue
}
}
func makeUIView(context: UIViewRepresentableContext<EmitterView>) -> SKView {
view.presentScene(scene)
return view
}
func updateUIView(_ uiView: SKView, context: UIViewRepresentableContext<EmitterView>) {
}
}
struct EmitterView_Previews: PreviewProvider {
static var previews: some View{
EmitterView()
}
}
class GameScene : SKScene{
var hue : Double = 0{
didSet{
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
}
}
var emitter: SKEmitterNode = SKEmitterNode()
override func didMove(to view: SKView) {
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
addChild(emitter)
}
}
The reason why this works is because on copy, the view
will copy the pointer, where as hue
will 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.
回答2:
In order to make the UIViewRepresentable struct mutable, add the @Binding prefix to the hue variable.
@Binding var hue: Double
You can then change the color of the particles from a parent View in the following manner:
struct ContentView: View {
@State var hue: Double = 0.5
var body: some View {
VStack {
EmitterView(hue: self.$hue)
Slider(value: $hue)
}
}
}
来源:https://stackoverflow.com/questions/57948554/how-can-i-get-skemitternode-to-work-in-swiftui