首页 > 解决方案 > 在 SceneKit (SwiftUI) 中初始化场景变化的材质

问题描述

我在我的 SwiftUI 应用程序中使用 SceneKit 来显示从 Blender 导出的.scn文件转换而来的一些文件。.dae

SceneKit 正在使用UIViewRepresentable.

我正在展示一步一步的指导指南,每个步骤都包含一些文本和动画。每个动画都是它自己的.scn文件,我正在根据当前步骤交换场景文件。

我的问题是我需要在交换场景时对材质进行一些初始化,但只有updateUIView在这种情况发生时才会被调用,而且我不想在这里做这项工作,因为每次动画暂停时都会调用它等。

每次更换场景时,我都想做一次这项工作,但我正在努力用我当前的实现找到解决方案。

我在想我处理这个场景交换的方式是不正确的,但我想知道是否有人能指出我正确的方向。

谢谢!

请参阅下面的完整代码:

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

标签: iosswiftswiftuiscenekit

解决方案


使用以下内容:

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

更新:有点纠正,但想法是一样的。使用 Xcode 11.4 / iOS 13.4 测试。SceneKitView更改时重新创建sceneFilePath:,因此如果未更新,则为不同的代码问题。

这是使用的测试视图:

struct TestSceneKitView: View {
    @State private var filePath = "test1"
    var body: some View {
        VStack {
            Button("Push") { self.filePath = "test2" }
            SceneKitView(sceneFilePath: filePath, pauseAnimation: .constant(false))
        }
    }
}

推荐阅读