How to use SCNVIew as a SubView of ARView?

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?
 }
Post not yet marked as solved Up vote post of GrandSir Down vote post of GrandSir
836 views

Replies

Only a single ARSession can be active at a time. This means that if SceneLocationView uses an ARSession under the hood, this will interfere with an ARView which creates an ARSession as well. You would need to render your AR content using the SceneLocationView. Alternatively, if you want to use ARView I suggest checking out Location Anchors. This lets you add location-based AR content as well (supported from iOS 15 onwards) without the need for a third party library. Supported regions for Location Anchors are continuously expanded. You can find a developer sample here: https://developer.apple.com/documentation/arkit/content_anchors/tracking_geographic_locations_in_ar.