2

I am working on an app that uses ARkit to detect images. When an image within the asset folder is detected, the app displays a swiftUI view on top of the image, and when an image is no longer tracked, the swiftUI view disappears. Up until here it all works fine.

Within the viewdidload method in the viewController file I am downloading and parsing a csv file from the internet. This also works.

Where I am struggling is to figure out how to pass the data parsed from the csv file that is within viewdidload to the swiftUI view, so that I can work with these data on the swiftUI view that I am creating. For example, I will show specific data depending on which image is detected.

I found other stackoverflow questions where they discuss how to pass data between viewcontrollers, but not between a viewcontroller and a swiftUI view.

Below is my code.

Here is my ViewController.swift file

import UIKit
import SceneKit
import ARKit
import SwiftUI

class ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Set the view's delegate
        sceneView.delegate = self
        
        // load csv file from dropbox
        let url = URL(string: "https://www.dropbox.com/s/0d0cr5o9rfxzrva/test.csv?dl=1")!
        let task = URLSession.shared.downloadTask(with: url) { location, response, error in
            guard let location = location else { return }
            do {
                // get path to directory
                let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
                print(documentDirectory.path)
                // giving name to file
                let name = (response as? HTTPURLResponse)?.suggestedFilename ?? location.lastPathComponent
                //create a destination url
                let destination = documentDirectory.appendingPathComponent(name)
                // check if file already exist
                if FileManager.default.fileExists(atPath: destination.path) {
                    //remove the file
                   try FileManager.default.removeItem(at: destination)
                }
                // move file from old to new url
                try FileManager.default.moveItem(at: location, to: destination)
                // reading the file
                let data = try String(contentsOf: destination, encoding: .utf8)
                
                //parsed csv
                let datos = self.csv(data: data)
                print(datos)
                
            } catch {
                print("ERROR", error)
            }
        }
        task.resume()
        
    }
    

    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // Create a session configuration
        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
        
        // Run the view's session
        sceneView.session.run(configuration)
    }
        
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // Pause the view's session
        sceneView.session.pause()
    }

    // MARK: - ARSCNViewDelegate
    

    // Override to create and configure nodes for anchors added to the view's session.
    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
        
        
        if let imageName = imageAnchor.referenceImage.name {
            imageController(for: planeNode, imageName: imageName)
            
        }
    
        
        let node = SCNNode()
        node.addChildNode(planeNode)
        return node
            
        }

   


    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)
        
    }
    
    }

    

func showImageView(hostingVC: UIHostingController<imageView>, on node: SCNNode) {
        
        let material = SCNMaterial()
        
        hostingVC.view.isOpaque = false
        material.diffuse.contents = hostingVC.view
        node.geometry?.materials = [material]
        
        hostingVC.view.backgroundColor = UIColor.clear
        
    }

    
// parsing csv method
    func csv(data: String) -> [[String]] {
        var result: [[String]] = []
        let rows = data.components(separatedBy: "\n")
        for row in rows {
            let columns = row.components(separatedBy: ",")
            result.append(columns)
            
        }
        return result
    }
    
   
}

and here is my swiftUI view file

import SwiftUI

struct imageView: View {

    var imageName: String

    var body: some View {
        
        ZStack{
        Color.white
            Text("hello \(imageName)")
            
        }
            
        
    }
}
7
  • Maybe you can try to make use of NotificationCenter or Combine Publishers in order to pass the data Commented Sep 23, 2020 at 13:23
  • 1
    You're already passing data between the controllers with imageName. Just add another var of the type you want to send to your imageView, add the same var and type to your ViewController. When you download csv, instead of creating a variable with let davos, set the property you just added on your controller and then send that var to your imageView the same way you are sending imageName. Commented Sep 23, 2020 at 13:58
  • thanks @clawesome. I thought so because I can pass imageName. However when I try to do the same with datos I get 'use of unresolved identifier'. I am clearly missing something. Would you be able to show on my code? Thank you. Commented Sep 23, 2020 at 14:09
  • What class type is datos? Commented Sep 23, 2020 at 14:20
  • It’s a multi-dimensional array [[String]] Commented Sep 23, 2020 at 14:24

1 Answer 1

1

You want datos to be passed to the constructor of the View, but the only datos you have in your code is a local variable in a closure.

You need to make a new property in the VC

var datos: [[String]]?

Then, when you get the data, save it in this property

self.datos = ....

Then, when you construct the View, pass self.datos

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. That worked. I was also missing passing datos to the imageController method

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.