2

I have been able to do this on SceneKit in the past for whatever reason this can't be done on RealityKit. This is some code I am using to test before adding to the app I am working on.

Updating AR Text from Header View text

So I have a header view and the header view updates the text as I type in new text. The problem is, it can't update the AR text inside ARView. In the image below, on the left you can see the header view matches the ARText. This is what you see initially on the screen.

The issue is the data is not updating continuously. The code already made the original text update to the header text once it runs, but it is not updating once with keyboard has changed the header text.

HOWEVER once I change the header text using my keyboard ( Image on the right ), the text does not update on the AR View

enter image description here enter image description here

I know the problem is I can't add the dollar sign $title or $title input in the way I do elsewhere which works.

For example here under Content View, clicking on the header text allows me to successfully change the text ( on the header view only ).

Content view - changing header text works

import SwiftUI
import RealityKit


struct ContentView : View {
    
    @State var title: String = "Default Title"
    @State private var titleInput: String = ""
  
    var body: some View {
        // Button on top of the screen
        
        VStack { HeaderView(title: $title)
            
            TextField("Insert Title", text: $titleInput)
                .textFieldStyle(.roundedBorder)
            Button(action: {
                title = titleInput
                titleInput = ""
            }, label: { Text("Change Title")})
            //Spacer()
        }//.padding()
        
        // AR screen space
        
        
        ARViewContainer(title: $title, titleInput: $titleInput).edgesIgnoringSafeArea(.all)
        
        
    }
    
    
}
//end of content view struct


//another view here as the header view
struct HeaderView: View {
    @Binding var title: String
    
    var body: some View {
        Text(title)
        //.padding(10)
    }
}

ARView:

The text successfully updates from the initial header view text, as per initial code below which is nice but not the final solution I seek, where AR text is constantly update by the keyboard.

Perhaps some of you might know the way around it. I will add what worked for me using scene kit in a bit.


//AR View Container
struct ARViewContainer: UIViewRepresentable {
    @Binding var title: String
    @Binding var titleInput: String
    
    func makeUIView(context: Context) -> ARView {
        //AR View Begins
        let arView = ARView(frame: .zero)
        
        //AR Content
        // Load the "Box" scene from the "Experience" Reality File
        let boxAnchor = try! Experience.loadBox()
        
        // Add the box anchor to the scene
        arView.scene.anchors.append(boxAnchor)
        
        
        let boxImg = ModelEntity(mesh: .generateBox(size: simd_make_float3(0.03, 0.01, 0.02)))
        if let texture = try? TextureResource.load(named: "texture") {
            var imageMaterial = UnlitMaterial()
            imageMaterial.baseColor = MaterialColorParameter.texture(texture)
            boxImg.model?.materials = [imageMaterial]
        }
        
        
        
        
        //end here what to bring to texture app starting above
        
        let text = MeshResource.generateText(title,
                                             extrusionDepth: 0.1,
                                             font: .systemFont(ofSize: 0.1),
                                             containerFrame: .zero,
                                             alignment: .center,
                                             lineBreakMode: .byWordWrapping)
        
        let shader = UnlitMaterial(color: .yellow)
        let textYellowEntity = ModelEntity(mesh: text, materials: [shader])
        textYellowEntity.position = simd_make_float3(-0.1, 0, -0.7)
        
        
        let textEntity = Entity()
        textEntity.setText("Please Update")
        
        //textYellowEntity.setText(title)
        let anchor = AnchorEntity()
        anchor.addChild(boxImg)
        anchor.addChild(textYellowEntity)
        anchor.addChild(textEntity)
        arView.scene.anchors.append(anchor)
        //End of AR Content
        
        return arView
        //AR View Ends
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {}
    
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
#endif

Extensions

extension Entity{
    
    /// Changes The Text Of An Entity
    /// - Parameters:
    ///   - content: String
    func setText(_ content: String){ self.components[ModelComponent] = self.generatedModelComponent(text: content) }
    
    /// Generates A Model Component With The Specified Text
    /// - Parameter text: String
    func generatedModelComponent(text: String) -> ModelComponent{
        
        let modelComponent: ModelComponent = ModelComponent(
            
            mesh: .generateText(text, extrusionDepth: TextElements().extrusionDepth, font: TextElements().font,
                                containerFrame: .zero, alignment: .center, lineBreakMode: .byTruncatingTail),
            
            materials: [SimpleMaterial(color: TextElements().colour, isMetallic: true)]
            
        )
        
        return modelComponent
    }
    
}

//--------------------
//MARK:- Text Elements
//--------------------

/// The Base Setup Of The MeshResource
struct TextElements{
   
    
    let initialText: String = "cube"
    let extrusionDepth: Float = 0.01
    let font: MeshResource.Font = MeshResource.Font.systemFont(ofSize: 0.05, weight: .bold)
    let colour: UIColor = .white
    
}

For reference, what worked for me in the past, using SceneKit

SceneKit updated the ARtext when I used inputText on the screen, as you can see below.

The ARtext updates as soon as I enter the text on the textfield

enter image description here [SceneKit screenshot3

  @IBOutlet weak var inputText: UITextField!
   @IBAction func updateAct(_ sender: Any) {
        sceneView.removeNodeByName(nodeName: "flyingText")
        sceneView.add3DText(showText: self.inputText!.text! ,nodeName: "flyingText")

        
        }
    
    
       // Set a delegate to track the number of plane anchors for providing UI feedback.
        sceneView.session.delegate = self
        sceneView.add3DText(showText: self.inputText!.text! )
        

extension

import UIKit
import ARKit
import SceneKit

extension ARSCNView {
    
    func removeNodeByName(nodeName: String)  {
        self.scene.rootNode.enumerateChildNodes { (node, _) in
            if node.name == nodeName {
                node.removeFromParentNode()
            }
        }
    }
    
    
    func add3DText(showText: String, nodeName: String = "flyingText")  {
        let inText = showText.isEmpty ? "Text not Avaiable!" : showText
        let text = SCNText(string: inText, extrusionDepth: 0.9)
        let material = SCNMaterial()
        //material.diffuse.contents = UIImage(named: randomMaterial())
        text.materials = [material]
        
        let node = SCNNode()
        let xpos = randomPosition(lowerBound: -0.5, upperBound: 0.5)
        let ypos = randomPosition(lowerBound: -0.5, upperBound: 0.5)
        node.position = SCNVector3(x: -0.5, y: -0.5, z: -0.3)
        //referenceNode.rotation = SCNVector4Make(0.01,0,0,.pi/2)
        node.rotation = SCNVector4Make(0.01,0,0,0)
        node.scale = SCNVector3(x: 0.01, y: 0.01, z: 0.01)
        node.geometry = text
        //node.name = nodeName
        node.name = nodeName
        
        self.scene.rootNode.addChildNode(node)
        //self.allowsCameraControl = true
        self.automaticallyUpdatesLighting = true
    
    }
    
    
    
    func randomPosition ( lowerBound lower:Float, upperBound upper:Float) -> Float {
        return Float(arc4random()) / Float(UInt32.max) * (lower - upper) + upper
    }
    
    
    
}




Update, we have the solution:

AS USUAL Andy Jazz's answer ( see his answer and code below ) works!!!

Here's the screenshots to prove it: Before and After inputting next text

enter image description here enter image description here

1 Answers1

1

It's not easy to achieve, even though the code is quite simple. I just copy-pasted this code from my recent project. Unfortunately I have no free time at the moment to adapt my code to your project. But everything works fine 100%.

import SwiftUI
import RealityKit

struct ContentView : View {
    @State var arView = ARView(frame: .zero)
    @State var title: String = "Default Title"
    @State var text: String = ""
    @State var entity = ModelEntity()
    @State var mesh = MeshResource.generateText("")
    @State var bool: Bool = false
    
    var body: some View {
        VStack {
            Text(title)
                .frame(alignment: .center)
                .font(.title)
            
            TextField("Enter text here:", text: $text)
                .padding([.horizontal], 20)
            
            Button("Change title") {
                if text != "" {
                    bool.toggle()
                    title = text
                    mesh = MeshResource.generateText(title,
                                      extrusionDepth: 0.01,
                                                font: .systemFont(ofSize: 0.12))
                    entity = ModelEntity(mesh: mesh,
                                    materials: [UnlitMaterial(color: .cyan)])
                    let xHalf = mesh.bounds.extents.x / 2
                    entity.position.x -= xHalf
                    text = ""
                }
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                    bool.toggle()
                }
            }

            ARViewContainer(arView: $arView, title: $title,
                                            entity: $entity, bool: $bool)
                .ignoresSafeArea()
                .onAppear {
                    bool.toggle()
                    mesh = MeshResource.generateText(title,
                                      extrusionDepth: 0.01,
                                                font: .systemFont(ofSize: 0.12))
                    entity = ModelEntity(mesh: mesh,
                                    materials: [UnlitMaterial(color: .yellow)])
                    let xHalf = mesh.bounds.extents.x / 2
                    entity.position.x -= xHalf
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        bool.toggle()
                    }
                }
        }
    }
}

struct ARViewContainer: UIViewRepresentable {
    @Binding var arView: ARView
    @Binding var title: String
    @Binding var entity: ModelEntity
    @Binding var bool: Bool
    var anchor = AnchorEntity()
    
    func makeUIView(context: Context) -> ARView {
        if bool {
            anchor.addChild(entity)
            anchor.position.z = -1.0
            arView.scene.anchors.append(anchor)
        }
        return arView
    }
    func updateUIView(_ arView: ARView, context: Context) {
        if bool {
            arView.scene.anchors.first?.removeFromParent()
            anchor.addChild(entity)
            anchor.position.z = -1.0
            arView.scene.anchors.append(anchor)
        }
        print("Anchors:", arView.scene.anchors.count)
    }
}

enter image description here

Andy Jazz
  • 49,178
  • 17
  • 136
  • 220