Learn Courses My Dashboard

Pass data between Coordinator and SwiftUI view

Hello everyone, I am requesting help on how I can pass variables between a coordinator class and a SwiftUI view.

This is my coordinator class:

class Coordinator: NSObject {
        
        private let view: SCNView
        init(_ view: SCNView) {
            self.view = view
            super.init()
        }
        
        @objc func search(sender: UITapGestureRecognizer) {
            
            let sceneView = sender.view as! SCNView
            let location = sender.location(in: sceneView)
            let results = sceneView.hitTest(location, options: [SCNHitTestOption.searchMode : 1])
            
            guard let hitTestResult = results.first else { return }
            let finalnode = hitTestResult.node
            
            var min = finalnode.boundingBox.min
            var max = finalnode.boundingBox.max
            @State var width = Double(max.x - min.x) //pass this variable to detail view
            DetailView(width: $width)
            
            
            
            let material = finalnode.geometry?.firstMaterial
            
            if let material = material {
                print("GREEN") //Prints "GREEN" every time, as expected
                material.diffuse.contents = UIColor.green
            }
            
        }
        
    }

This is my SwiftUI View:

struct DetailView: View {
    @Binding var width: Double
    var body: some View {
        VStack{
            CustomSceneView(scene:{
                let scene = SCNScene(named: "Robot.usdz")!
                let cameraNode = SCNNode()
                cameraNode.camera = SCNCamera()
                scene.rootNode.addChildNode(cameraNode)
                
                // place the camera
                cameraNode.position = SCNVector3(x: -4, y: 3, z: 5.5)
                //cameraNode.rotation = SCNVector4(x: 0, y: -1, z: 0, w: 0.5)
                cameraNode.rotation = SCNVector4(x: -1, y: -1.2, z: 0, w: .pi/4)
                scene.rootNode.addChildNode(cameraNode)
                return scene
            }(), options: [.autoenablesDefaultLighting]).frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/1.95,alignment: .top)
            
            Text(String(width)) //Display the variable here
        }.ignoresSafeArea(.all).edgesIgnoringSafeArea(.all)
    }
}

How can I achieve the wanted result?

As an observation, in SwiftUI normally you would declare a @State variable in say, ViewA, and then a @Binding to it in, say, ViewB where you are going to be changing the value in ViewB and have that change reflected back in ViewA where the State variable was declared.

In the case you have I am guessing that you are not changing the value in the DetailView so there is no need to declare it as a Binding there.

Have you tried declaring the value you are passing in as just a var in both cases?

So in your Coordinator just declare it as a var:

var width = Double(max.x - min.x) //pass this variable to detail view
DetailView(width: width) //  Without the $ sign.

and in your DetailView declare is as a var as well.

struct DetailView: View {
    var width: Double  // Without the @Binding property wrapper

    var body: some View {

This is a useful discussion on property wrappers

Hey Chris! Thank you for your reply! I managed to make it work as following. I have declared a Binding in UIViewRepresentable and one in my coordinator, and added a new parameter to the init function in my coordinator! Lastly I have declared a State variable in my DetailView, and now everything is synced as it is supposed! I will give your solution a shot! Thank you for your help, and for your time

Good news.