Home > Back-end >  How to use SCNView as a SubView of ARView?
How to use SCNView as a SubView of ARView?

Time:11-21

I'm using ARKit-CoreLocation library to present POIs in AR World, in iOS 14. But the thing is, I am not able to use ARCL's SceneLocationView because it is SCNView. So when I add it as a subview, it overlaps my ARView contents and creates a new ARSession, leaving my ARView in the background.

Code:

extension RealityKitViewController {
    typealias Context = UIViewControllerRepresentableContext<RealityKitViewControllerRepresentable>
}

class RealityKitViewController: UIViewController {
    let sceneLocationView = SceneLocationView()
    let arView = ARView(frame: .zero)
    let context : Context
    let pins: [Pin]
    
    var currentLocation : CLLocation? {
        return sceneLocationView.sceneLocationManager.currentLocation
    }
    
    init (_ context : Context, pins: [Pin]) {
        self.context = context
        self.pins = pins
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func makeArView()
    {
        // Start AR session
        let session = arView.session
        let config = ARWorldTrackingConfiguration()
        config.planeDetection = [.horizontal, .vertical]
        session.run(config)
        
        // Add coaching overlay
        let coachingOverlay = ARCoachingOverlayView()
        coachingOverlay.session = session
        coachingOverlay.goal = .horizontalPlane
        coachingOverlay.delegate = context.coordinator
        
        
        arView.addSubview(coachingOverlay)
        
        arView.debugOptions = [.showFeaturePoints, .showAnchorOrigins, .showAnchorGeometry]
        
        // Handle ARSession events via delegate
        context.coordinator.view = arView
        session.delegate = context.coordinator
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // probably problem here
        sceneLocationView.frame = view.bounds
        arView.frame = sceneLocationView.bounds
        
        sceneLocationView.addSubview(arView)
        view.addSubview(sceneLocationView)
        addPins()
    }
    
    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        sceneLocationView.frame = view.bounds
    }
    
    override func viewWillAppear(_ animated: Bool) {
        DispatchQueue.main.asyncAfter(deadline: .now()   0.2) {
            super.viewWillAppear(animated)
            self.sceneLocationView.run()
        }
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        sceneLocationView.pause()
    }
    
    func addPins() {
        guard let currentLocation = currentLocation, currentLocation.horizontalAccuracy < 16 else {
            return DispatchQueue.main.asyncAfter(deadline: .now()   0.5) { [weak self] in
                self?.addPins()
            }
        }
        
        self.pins.forEach { pin in
            guard pin.isLocation else { return }
            guard let location = pin.location else { return assertionFailure() }
            guard let image = UIImage(named: pin.image) else { return assertionFailure() }
            
            let node = LocationAnnotationNode(location : location, image: image)
            
            node.scaleRelativeToDistance = true
            sceneLocationView.addLocationNodeWithConfirmedLocation(locationNode: node)
        }
    }
}

// if you want to test it, you can try to place these pins to a location where you can easily get coordinates from Google Earth.
struct RealityKitViewControllerRepresentable : UIViewControllerRepresentable {
    let pins = [Pin(image: "test", location: CLLocation(coordinate: CLLocationCoordinate2D(latitude: 0.03275742958, longitude: 0.32827424), altitude: 772.1489524841309), isLocation: true)]
    @Binding var arActivate : Bool
    
    func makeUIViewController(context: Context) -> RealityKitViewController {
        let viewController = RealityKitViewController(context, pins: pins)
        
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: RealityKitViewController, context: Context) {
        
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
}

class Coordinator: NSObject, ARSessionDelegate {
    weak var view: ARView?
 }

CodePudding user response:

I solved the problem: There's no solution.

Using SCNView as a subview is a really horrible idea yet every answer I found says "no", it seems like this feature will remin unresolved until Apple releases an integration or something for that.

  • Related