I have code that detects images via ARkit. When an image is detected, a swiftUI view is placed on top of the image. My code can detect the images, however I am only able to overlay one swiftUI view. I'd like the code to be responsive so that it can identify the image and place the appropriate view on top of it. Code below:
Code for SwiftUI imageOne.swift file
Code for SwiftUI imageTwo.swift file
Code for ViewController.swift file
The images on my AR Resource are also called imageOne and imageTwo, same each of the swiftUI views
Code for SwiftUI imageOne.swift file
Code Block import SwiftUI struct imageOne: View { var body: some View { Text("hello imageOne") } }
Code for SwiftUI imageTwo.swift file
Code Block import SwiftUI struct imageTwo: View { var body: some View { Text("hello imageTwo") } }
Code for ViewController.swift file
Code Block swift import UIKit import SceneKit import ARKit import SwiftUI class ViewController: UIViewController, ARSCNViewDelegate { @IBOutlet var sceneView: ARSCNView! override func viewDidLoad() { super.viewDidLoad() sceneView.delegate = self } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) let configuration = ARImageTrackingConfiguration() guard let trackingImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else { fatalError("Couldn't load tracking images") } configuration.trackingImages = trackingImages configuration.maximumNumberOfTrackedImages = 2 sceneView.session.run(configuration) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) sceneView.session.pause() } func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? { guard let imageAnchor = anchor as? ARImageAnchor else {return nil} let plane = SCNPlane(width: imageAnchor.referenceImage.physicalSize.width, height: imageAnchor.referenceImage.physicalSize.height) let planeNode = SCNNode(geometry: plane) planeNode.eulerAngles.x = -.pi / 2 imageOneController(for: planeNode) let node = SCNNode() node.addChildNode(planeNode) return node } func imageOneController(for node: SCNNode) { let imageOneView = UIHostingController(rootView: imageOne()) DispatchQueue.main.async { imageOneView.willMove(toParent: self) self.addChild(imageOneView) imageOneView.view.frame = CGRect(x: 0, y: 0, width: 500, height: 500) self.view.addSubview(imageOneView.view) self.showImageOne(hostingVC: imageOneView, on: node) } } func imageTwoController(for node: SCNNode) { let imageTwoView = UIHostingController(rootView: imageTwo()) DispatchQueue.main.async { imageTwoView.willMove(toParent: self) self.addChild(imageTwoView) imageTwoView.view.frame = CGRect(x: 0, y: 0, width: 500, height: 500) self.view.addSubview(imageTwoView.view) self.showImageTwo(hostingVC: imageTwoView, on: node) } } func showImageOne(hostingVC: UIHostingController<imageOne>, on node: SCNNode) { let material = SCNMaterial() hostingVC.view.isOpaque = false material.diffuse.contents = hostingVC.view node.geometry?.materials = [material] hostingVC.view.backgroundColor = UIColor.clear } func showImageTwo(hostingVC: UIHostingController<imageTwo>, on node: SCNNode) { let material = SCNMaterial() hostingVC.view.isOpaque = false material.diffuse.contents = hostingVC.view node.geometry?.materials = [material] hostingVC.view.backgroundColor = UIColor.clear } }
The images on my AR Resource are also called imageOne and imageTwo, same each of the swiftUI views
Hi @Josedv,
Thanks for your reply. For some reason, I seem to be unable to edit my original post, but I caught a few typos that are likely what is preventing you from running the code (including what is causing the error message you indicated). For brevity, make a few changes;
Whereas I had noted your function should be;
You will want to change that to;
That was a typo on my part, as imageName is not a type; the imageName you are providing from your ARReferenceImage is a String.
Subsequently, there are two other things you should change (one for clarity and one that actually is going to lead to an error). In the same function, which is now func imageController(for node: SCNNode, imageName: String), you will want to change the name of the first variable, defining the UIHostingController, as the name imageView is being used for both defining the UIHostingController and your SwiftUI view, which is confusing. Subsequently, when calling the SwiftUI view, the name of the variable you are passing should be imageName and not name. As such, that whole function should look like this;
This, alongside what was mentioned yesterday, should get you up and running. I wish I could edit my previous post (maybe I'm just overlooking how to do that), but I gave all of this code a try and was able to compile without any issues.
Thanks for your reply. For some reason, I seem to be unable to edit my original post, but I caught a few typos that are likely what is preventing you from running the code (including what is causing the error message you indicated). For brevity, make a few changes;
Whereas I had noted your function should be;
Code Block func imageController(for node: SCNNode, imageName: imageName)
You will want to change that to;
Code Block func imageController(for node: SCNNode, imageName: String)
That was a typo on my part, as imageName is not a type; the imageName you are providing from your ARReferenceImage is a String.
Subsequently, there are two other things you should change (one for clarity and one that actually is going to lead to an error). In the same function, which is now func imageController(for node: SCNNode, imageName: String), you will want to change the name of the first variable, defining the UIHostingController, as the name imageView is being used for both defining the UIHostingController and your SwiftUI view, which is confusing. Subsequently, when calling the SwiftUI view, the name of the variable you are passing should be imageName and not name. As such, that whole function should look like this;
Code Block func imageController(for node: SCNNode, imageName: String) { let imgView = UIHostingController(rootView: imageView(imageName: imageName)) DispatchQueue.main.async { imgView.willMove(toParent: self) self.addChild(imgView) imgView.view.frame = CGRect(x: 0, y: 0, width: 500, height: 500) self.view.addSubview(imgView.view) self.showImageView(hostingVC: imgView, on: node) } }
This, alongside what was mentioned yesterday, should get you up and running. I wish I could edit my previous post (maybe I'm just overlooking how to do that), but I gave all of this code a try and was able to compile without any issues.