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
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
@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