Search code examples
buttonlabelarkitviewdidappear

change text in arkitview via button(swift4)


My code uses arkit. All I want to do is when I change the text of the label. The change is reflected in the arkit text. Currently its not. Whatever the arkit label stays and default and never changes no matter what is on the label on the view controller.

        import UIKit;import ARKit
class ViewController: UIViewController {

@IBOutlet var arkitView:ARSCNView!
@IBOutlet var outputImageView:UIImageView!
@IBOutlet var ffox: UILabel!
@IBOutlet var enterText: UITextField!
var textGeometry: SCNText!

//4. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//5. Create Our Session
let augmentedRealitySession = ARSession()

override func viewDidAppear(_ animated: Bool) {

    arkitView.session.run(configuration)
    //1. Create The Text
    textGeometry = SCNText(string: ffox.text!, extrusionDepth: 1)

    //2. Set It From The UILabel
    textGeometry.string = ffox.text!

    //3. Create A Material For The Text
    let material = SCNMaterial()
    material.diffuse.contents = UIColor.green
    textGeometry.materials = [material]

    //4. Create A Holder Node To Hold The Text
    let node = SCNNode()
    node.position = SCNVector3(x: 0, y: 0.02, z: -0.01)
    node.scale = SCNVector3(x: 0.01, y: 0.01, z: 0.01)
    node.geometry = textGeometry

    arkitView.scene.rootNode.addChildNode(node)
    arkitView.automaticallyUpdatesLighting = true

    DispatchQueue.main.async {
        self.textGeometry.string = self.ffox.text!
    }
    }
@IBAction func takeScreenshotAction() {
    if let d = enterText.text
    {
        ffox.text = d
    }}}

Solution

  • Looking at your code you may want to set the Font and Font Size e.g:

    textGeometry.font = UIFont(name: "Helvatica", size: 3)
    

    Changing The Text:

    To change the text of SCNText you need to access it's string property.

    Declare you SCNText as a var above your Class Definition e.g.

    let textGeometry: SCNText!
    

    And personally I would initialize it in ViewDidLoad.

    Then to change it you simply call:

    textGeometry.string = "I Have Changed The String"
    

    So in your example assuming as I said that your SCNText is called textGeometry you can do something like this:

    textGeometry.string = ffox.text
    

    An Example Of Creating An SCNText Geometry:

    class Text: SCNNode{
    
    var textGeometry: SCNText!
    
    /// Creates An SCNText Geometry
    ///
    /// - Parameters:
    ///   - text: String (The Text To Be Displayed)
    ///   - depth: Optional CGFloat (Defaults To 1)
    ///   - font: UIFont
    ///   - textSize: Optional CGFloat (Defaults To 3)
    ///   - colour: UIColor
    init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
    
        super.init()
    
        //1. Create The Text Geometry With String & Depth Parameters
        textGeometry = SCNText(string: text , extrusionDepth: depth)
    
        //2. Set The Font With Our Set Font & Size
        textGeometry.font = UIFont(name: font, size: textSize)
    
        //3. Set The Flatness To Zero (This Makes The Text Look Smoother)
        textGeometry.flatness = 0
    
        //4. Set The Colour Of The Text
        textGeometry.firstMaterial?.diffuse.contents = colour
    
        //5. Set The Text's Material
        self.geometry = textGeometry
    
        //6. Scale The Text So We Can Actually See It!
        self.scale = SCNVector3(0.01, 0.01 , 0.01)
    }
    
       required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
       }
    }
    

    Using This Class In Your Scene:

        //1. Create An SCNText With A White Colour
        let nodeToAdd = Text(text: "Hello AR", colour: .white)
    
        //2. Add The Text To The Scene
        augmentedRealityView.scene.rootNode.addChildNode(nodeToAdd)
    
        //3. Set The Text's Position Center On The Screen & 1.5m In Front Of The Camera
        nodeToAdd.position = SCNVector3(0, 0, -1.5)
    

    Hope this helps...

    Update:

    The code above works perfectly however since your are having trouble implementing it, here is how you code should look:

    class Example: UIViewController {
    
    //1. Create A Reference To Our ARSCNView In Our Storyboard Which Displays The Camera Feed
    @IBOutlet weak var augmentedRealityView: ARSCNView!
    
    //2. Create A Label To Display Text
    @IBOutlet weak var ffox: UILabel!
    
    //3. Create The Text Gemoetry
    var textGeometry: SCNText!
    
    //4. Create Our ARWorld Tracking Configuration
    let configuration = ARWorldTrackingConfiguration()
    
    //5. Create Our Session
    let augmentedRealitySession = ARSession()
    
    
    //--------------------
    //MARK: View LifeCycle
    //--------------------
    
    override func viewDidLoad() {
    
        super.viewDidLoad()
    
    }
    
    override func viewDidAppear(_ animated: Bool) {
    
        augmentedRealityView.session.run(configuration)
    
        //1. Create The Text
        textGeometry = SCNText(string: ffox.text!, extrusionDepth: 1)
    
        //2. Set It From The UILabel
        textGeometry.string = ffox.text!
    
        //3. Create A Material For The Text
        let material = SCNMaterial()
        material.diffuse.contents = UIColor.green
        textGeometry.materials = [material]
    
        //4. Create A Holder Node To Hold The Text
        let node = SCNNode()
        node.position = SCNVector3(x: 0, y: 0.02, z: -0.01)
        node.scale = SCNVector3(x: 0.01, y: 0.01, z: 0.01)
        node.geometry = textGeometry
    
        augmentedRealityView.scene.rootNode.addChildNode(node)
        augmentedRealityView.automaticallyUpdatesLighting = true
    
    }
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    
       }
    
    }
    

    I expect the reason your app is crashing is perhaps you have not setup the ffox text in Interface Builder and Wired it up as in the image below:

    enter image description here

    Also remember the UILabel must contain text!

    Final Update:

    Since Sam has still not been able to get this to work, I am writing a more in-depth example, which does the following: (1) The text in a UILabel will be displayed in an SCNTextGeometry on Load. (2) You can change the string of the SCNTextGeometry by pressing one of three buttons, (3) You can change the string of the SCNTextGeometry by using a UITextField Input.

    All of this has been fully tested and works as does my other example.

    You can download the full example here: Sample Project

    And of course the `ViewController' Class:

    import UIKit
    import ARKit
    
    //--------------------
    //MARK: TextNode Class
    //--------------------
    
    class TextNode: SCNNode{
    
        var textGeometry: SCNText!
    
        /// Creates An SCNText Geometry
        ///
        /// - Parameters:
        ///   - text: String (The Text To Be Displayed)
        ///   - depth: Optional CGFloat (Defaults To 1)
        ///   - font: UIFont
        ///   - textSize: Optional CGFloat (Defaults To 3)
        ///   - colour: UIColor
        init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
    
            super.init()
    
            //1. Create The Text Geometry With String & Depth Parameters
            textGeometry = SCNText(string: text , extrusionDepth: depth)
    
            //2. Set The Font With Our Set Font & Size
            textGeometry.font = UIFont(name: font, size: textSize)
    
            //3. Set The Flatness To Zero (This Makes The Text Look Smoother)
            textGeometry.flatness = 0
    
            //4. Set The Colour Of The Text
            textGeometry.firstMaterial?.diffuse.contents = colour
    
            //5. Set The Text's Material
            self.geometry = textGeometry
    
            //6. Scale The Text So We Can Actually See It!
            self.scale = SCNVector3(0.01, 0.01 , 0.01)
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    //--------------------------
    //MARK: UITextFieldDelegate
    //--------------------------
    
    extension ViewController: UITextFieldDelegate{
    
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    
            print("Current Text = \(textField.text!)")
    
            DispatchQueue.main.async {
    
                self.textNode.textGeometry.string = textField.text!
            }
    
            return true
        }
    
    
    }
    
    class ViewController: UIViewController {
    
        //1. Create A Reference To Our ARSCNView In Our Storyboard Which Displays The Camera Feed
        @IBOutlet weak var augmentedRealityView: ARSCNView!
    
        //2. Create A Reference To Our SCNNode
        var textNode: TextNode!
    
        //2. Create A Label To Display Text
        @IBOutlet weak var ffox: UILabel!
    
        //3. Create UITextField So Text Can Be Changed Dynamically
        @IBOutlet weak var textChangerField: UITextField!
    
        //3. Create Our ARWorld Tracking Configuration
        let configuration = ARWorldTrackingConfiguration()
    
        //3. Create Our Session
        let augmentedRealitySession = ARSession()
    
    
        //--------------------
        //MARK: View LifeCycle
        //--------------------
    
        override func viewDidLoad() {
    
            super.viewDidLoad()
    
        }
    
        override func viewDidAppear(_ animated: Bool) {
    
            augmentedRealityView.session.run(configuration)
    
            //1. Create An SCNText With A White Colour & With The Label's Text
            textNode = TextNode(text: ffox.text!, colour: .white)
    
            //2. Add The Text To The Scene
            augmentedRealityView.scene.rootNode.addChildNode(textNode)
    
            //3. Set The Text's Position Center On The Screen & 1.5m In Front Of The Camera
            textNode.position = SCNVector3(0, 0, -1.5)
    
            //4. Set The Lighting
            augmentedRealityView.automaticallyUpdatesLighting = true
    
            //5. Assign The Delegate To The TextChangerField
            textChangerField.delegate = self
    
    
        }
    
    
        /// Changes The Text In The Button At The Bottom Of The Screen
        ///
        /// - Parameter sender: UIButton
        @IBAction func changeText(_ sender: UIButton){
    
            guard let textToDisplay = sender.titleLabel?.text else { return }
    
            DispatchQueue.main.async {
    
                self.textNode.textGeometry.string = textToDisplay
            }
    
        }
    
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
    
        }
    
    }
    

    In Interface Builder you will have the following:

    enter image description here