Search code examples
swiftswiftuiaugmented-realityscenekitarkit

SCNNode is not showing up


I'm new in Swift and ARKit. For some reason the SCNNode node I'm trying to display is not showing up. I'm working with SwiftUI. I defined in the next code block the function addNode that should render the node.

import Foundation
import ARKit
import SwiftUI

// MARK: - ARViewIndicator
struct ARViewIndicator: UIViewControllerRepresentable {
   typealias UIViewControllerType = ARView
   
   func makeUIViewController(context: Context) -> ARView {
      return ARView()
   }
   func updateUIViewController(_ uiViewController:
   ARViewIndicator.UIViewControllerType, context:
   UIViewControllerRepresentableContext<ARViewIndicator>) { }
}


class ARView: UIViewController, ARSCNViewDelegate {
    var arView: ARSCNView {
        return self.view as! ARSCNView
    }
    
    override func loadView() {
     self.view = ARSCNView(frame: .zero)
    }
    
    override func viewDidLoad() {
      super.viewDidLoad()
      arView.delegate = self
      arView.scene = SCNScene()
    }

    // MARK: - Functions for standard AR view handling
   override func viewDidAppear(_ animated: Bool) {
      super.viewDidAppear(animated)
   }
    
   override func viewDidLayoutSubviews() {
      super.viewDidLayoutSubviews()
   }
    
   override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let configuration = ARWorldTrackingConfiguration()
        arView.debugOptions = [.showFeaturePoints, 
                               .showWorldOrigin]
        arView.session.run(configuration)
        arView.delegate = self
   }
    
   override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      arView.session.pause()
   }
    
    func addNode(){
        let node = SCNNode()
        node.geometry = SCNBox(width: 0.1, 
                              height: 0.1, 
                              length: 0.1, 
                       chamferRadius: 0)
        node.geometry?.firstMaterial?.diffuse.contents = UIColor.blue
        node.position = SCNVector3(0,0,0.3)
        arView.scene.rootNode.addChildNode(node)
        arView.delegate = self
        
        print(123)
    }
    
   // MARK: - ARSCNViewDelegate
   func sessionWasInterrupted(_ session: ARSession) {}
   
   func sessionInterruptionEnded(_ session: ARSession) {}
   func session(_ session: ARSession, didFailWithError error: Error)
   {}
   func session(_ session: ARSession, cameraDidChangeTrackingState
   camera: ARCamera) {}
    
}

... and that function is invoked when clicking the button "HOME"

import SwiftUI
import ARKit

// MARK: - NavigationIndicator
struct NavigationIndicator: UIViewControllerRepresentable {
   typealias UIViewControllerType = ARView
   func makeUIViewController(context: Context) -> ARView {
      return ARView()
   }
   func updateUIViewController(_ uiViewController:
   NavigationIndicator.UIViewControllerType, context:
   UIViewControllerRepresentableContext<NavigationIndicator>) { }
}

struct ContentView: View {
   @State var page = "Home"
   
   var body: some View {
      VStack {
        ZStack {
           NavigationIndicator()
            VStack {
                Spacer()
                HStack {
                    Button("Home") {
                        let ar = ARView();
                        ar.addNode()                        
                    }.padding()
                     .background(RoundedRectangle(cornerRadius: 10)
                     .foregroundColor(Color.white).opacity(0.7))
                    Spacer()
               }
          }
        }
      }
   }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Do you know why it's not showing up ? Thanks in advance !


Solution

  • Use this approach for SceneKitView:

    import SwiftUI
    import ARKit
    
    struct SceneKitView: UIViewRepresentable {
        
        let arView = ARSCNView(frame: .zero)
        @Binding var pressed: Bool
        @Binding var node: SCNNode
        
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
        
        final class Coordinator: NSObject, ARSCNViewDelegate {
            
            var control: SceneKitView
            
            init(_ control: SceneKitView) {
                self.control = control
            }
            
            func renderer(_ renderer: SCNSceneRenderer,
                   updateAtTime time: TimeInterval) {
                
                if control.pressed {
                    self.control.node = self.addCube()
                    self.control.arView.scene.rootNode.addChildNode(control.node)
                }
            }
            
            fileprivate func addCube() -> SCNNode {
                control.node.geometry = SCNBox(width: 0.25,
                                              height: 0.25,
                                              length: 0.25,
                                       chamferRadius: 0.01)
                control.node.geometry?.firstMaterial?.diffuse.contents = UIColor.blue
                control.node.geometry?.firstMaterial?.lightingModel = .phong
                control.node.position = SCNVector3(0, 0,-2)
                return control.node
            }
        }
            
        func makeUIView(context: Context) -> ARSCNView {
            arView.scene = SCNScene()
            arView.delegate = context.coordinator
            arView.autoenablesDefaultLighting = true
            arView.debugOptions = .showFeaturePoints
            // arView.allowsCameraControl = true
            
            let config = ARWorldTrackingConfiguration()
            arView.session.run(config)
            
            return arView
        }
        
        func updateUIView(_ uiView: ARSCNView,
                           context: Context) { }
    }
    

    Then use this code for ContentView.

    struct ContentView: View {
        
        @State var pressed: Bool = false
        @State var node = SCNNode()
           
        var body: some View {
            ZStack {
                SceneKitView(pressed: $pressed, node: $node)
                VStack {
                    Spacer()
                    HStack {
                        Button("Blue Cube") {
                            pressed.toggle()
                        }.padding()
                         .foregroundColor(.red)
    
                        Spacer()
                    }
                }
            }
        }
    }
    

    enter image description here

    P.S.

    However, a strange issue occurs with ARSCNView in Simulator – after pressing a button a SCNBox appears only after tapping a screen with .allowsCameraControl = true.