0

Maybe I don't understand UIViewRepresentable correctly but I thought this would work.

I'm trying to use two classes that are UIViewRepresentable to display my scnkit scenes in SwiftUI. I also use two coordinator methods for the classes, one each. The coordinator has access to the rendering and physicsWorld methods for the current scene.

The player spawns in the first scene and when contact is made with an enemy, it transitions into the second scene. Here is where the problem starts, it doesn't let the second coordinator handle the physicsWorld and rendering methods for the second class.

I believe it's because I'm still using the first class in swiftui and not the second even though the scene changes. Or in other words, I'm changing scenes but not the class being used.

The Question: How can I transition from the first class to the second and/or from the first scene to the other and still have the coordinator active for each.

Here is the First Class.

struct FirstClass : UIViewRepresentable {

    var view = SCNView()
    var scene = SCNScene(named: "First.scn")!

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> SCNView {
        scene.physicsWorld.contactDelegate = context.coordinator
        view.delegate = context.coordinator
        return view
    }

    ...

    /// Transitions into the second scene.
    func changeScenes() {
        controlManager.jumped = false
        let transition = SKTransition.fade(with: .black, duration: 0.5)
        let secondClass = SecondClass()
         view.present(secondClass.scene, with: transition, incomingPointOfView: nil)
    }
}

Here is the Second Class

struct SecondClass : UIViewRepresentable {

    var view = SCNView()
    var scene = SCNScene(named: "Second.scn")!

    func makeCoordinator() -> SecondCoordinator {
        SecondCoordinator(self)
    }

    func makeUIView(context: Context) -> SCNView {
        scene.physicsWorld.contactDelegate = context.coordinator
        view.delegate = context.coordinator
        return view
    }
}

Here is the SwiftUI Class

struct Game: View {

    var view = FirstClass()

    var body: some View {
        ZStack {
            view
            
            ...
        }
    }
}
James Castrejon
  • 567
  • 6
  • 16

1 Answers1

0

This part let secondClass = SecondClass() won't work, because SwiftUI is not aware about the instance of SecondClass, in order to properly manage its lifecycle. More, you don't seem to use view property of the SecondClass struct (btw, interesting name for a struct :) )

One solution is to manage both scenes within the same UIViewRepresentable, basically keeping the lifecycle management on the same side.

struct MyScene : UIViewRepresentable {

    var view = SCNView()
    var firstScene = SCNScene(named: "First.scn")!
    lazy var secondScene = SCNScene(named: "Second.scn")!

    ...

    /// Transitions into the second scene.
    func changeScenes() {
        controlManager.jumped = false
        let transition = SKTransition.fade(with: .black, duration: 0.5)
        view.present(secondScene, with: transition, incomingPointOfView: nil)
    }
}
Cristik
  • 30,989
  • 25
  • 91
  • 127
  • This could work if both of the scenes used the same coordinator and you'll need to update the makeUI method to include and setup both scenes. You'll also need to include code from both views to manage the scenes and their nodes (this will quickly fill up the struct with tons of code). – James Castrejon Jun 03 '21 at 14:24
  • The name SecondClass is the name I used for the example, not necessarily what I actually used. You do make a fair point though. – James Castrejon Jun 03 '21 at 14:25
  • @JamesCastrejon you can keep the two coordinators, in order to keeps the concerns separated, and only do the assignments in this “view”. – Cristik Jun 03 '21 at 16:16