I'm using SceneKit in my SwiftUI app to display some .scn
files converted from .dae
files exported from Blender.
The SceneKit is being displayed using UIViewRepresentable
.
I'm displaying a step by step instructional guide, and each step contains some text and an animation. Each animation is its own .scn
file and I'm swapping the scene file based on the current step.
My issue is I need to perform some initialisation of materials when the scene is swapped but only updateUIView
is called when this happens, and I don't want to be doing this work here as this gets called each time the animation is paused etc.
I want to be doing this work once each time the scene is replaced but I'm struggling to find a solution to this with my current implementation.
I'm thinking the way I'm handling this scene swapping is incorrect but I'm wondering if someone can point me in the right direction.
Thanks!
See full code below:
struct MaintenanceFlowDetailView: View {
var maintenanceFlow: MaintenanceFlow
@State private var currentStepIndex = 0
@State private var pauseAnimation = false
func stepBackwards() {
currentStepIndex -= 1
pauseAnimation = false
}
func stepForwards() {
currentStepIndex += 1
pauseAnimation = false
}
func togglePauseAnimation() {
pauseAnimation = !pauseAnimation
}
var body: some View {
NavigationView{
VStack(alignment: .leading, spacing: 12){
SceneKitView(sceneFilePath: maintenanceFlow.steps[currentStepIndex].scenePath, pauseAnimation: $pauseAnimation)
.frame(height: 300)
Text(maintenanceFlow.steps[currentStepIndex].text)
Spacer()
HStack{
if currentStepIndex > 0 {
Button(action: stepBackwards) {
Text("Back")
}
}
Spacer()
Button(action: togglePauseAnimation) {
Text(pauseAnimation ? "Play" : "Pause")
}
Spacer()
if currentStepIndex < maintenanceFlow.steps.count - 1 {
Button(action: stepForwards) {
Text("Next")
}
}
}
}.padding()
.navigationBarTitle(Text(maintenanceFlow.name))
}
}
}
struct SceneKitView : UIViewRepresentable {
var sceneFilePath: String
@Binding var pauseAnimation: Bool
func makeUIView(context: Context) -> SCNView {
print("calling CREATE view")
// I NEED TO INITIALISE MATERIALS ETC JUST ONCE HERE...
let scnView = SCNView()
return scnView
}
func updateUIView(_ scnView: SCNView, context: Context) {
print("calling UPDATE view")
// BUT THE SCENE IS REPLACED HERE :(
let scene = SCNScene(named: sceneFilePath)!
scene.isPaused = pauseAnimation
scnView.scene = scene
scnView.showsStatistics = true
}
}
Use the following:
struct SceneKitView : UIViewRepresentable {
var sceneFilePath: String
@Binding var pauseAnimation: Bool
func makeUIView(context: Context) -> SCNView {
print("calling CREATE view")
let scnView = SCNView()
let scene = SCNScene(named: sceneFilePath)
scnView.scene = scene
scnView.showsStatistics = true
return scnView
}
func updateUIView(_ scnView: SCNView, context: Context) {
print("calling UPDATE view")
scnView.scene?.isPaused = pauseAnimation
}
}
Update: a bit corrected, but idea is the same. Tested with Xcode 11.4 / iOS 13.4. SceneKitView
is recreated when sceneFilePath:
is changed, so if it is not updated, then it is different code issue.
Here is testing view used:
struct TestSceneKitView: View {
@State private var filePath = "test1"
var body: some View {
VStack {
Button("Push") { self.filePath = "test2" }
SceneKitView(sceneFilePath: filePath, pauseAnimation: .constant(false))
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.