Search code examples
swiftswiftuiaugmented-realityvirtual-realityrealitykit

RealityKit – Why ModelEntity doesn't change dynamically?


I am building an AR App. Here is my code

Content View:

@State var timeAccumulate = 0
let styleCount = 2
let timer = Timer.publish(every: 10, on: .main, in: .common).autoconnect()
var body: some View {
let vc = ARViewContainer(timeAccumulate: timeAccumulate)
   .edgesIgnoringSafeArea(.all)
   .onAppear(perform: { getData() })
   .onReceive(timer) { _ in
        timeAccumulate = (timeAccumulate + 1) % styleCount
   }
   return vc
}

ARViewContainer:

struct ARViewContainer: UIViewRepresentable {
    var timeAccumulate: Int
    let boxAnchor = try! Experience.loadBox()
    let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    
    func makeUIView(context: Context) -> ARView {
        let arView = ARView(frame: .zero)
        arView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
        
        arView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        
        for i in 1...(imageOrder.count+1-1) {
            let entity = boxAnchor.findEntity(named: "box\(i)")!
       
            let modelEntity = entity.children[0]
            modelEntity.generateCollisionShapes(recursive: true)
            arView.installGestures([.all], for: modelEntity as! Entity & HasCollision)
        }
        
        arView.scene.anchors.append(boxAnchor)
        
        return arView
    }
 
    func updateUIView(_ uiView: ARView, context: Context) {
        print(timeAccumulate)
        for i in 1...(imageOrder.count+1-1) {
            let boxThis = boxAnchor.findEntity(named: "box\(i)")!
            var modelEntity: ModelEntity!

            if timeAccumulate == 0 {
                modelEntity = try! ModelEntity.loadModel(named: "Box", in: Bundle.main)
                boxThis.children[0] = modelEntity
            } else {
                modelEntity = try! ModelEntity.loadModel(named: "Chess1", in: Bundle.main)
                boxThis.children[0] = modelEntity
            }
            do {
                var material = SimpleMaterial()
                
                let location = documents.appendingPathComponent("\(i).jpg")
                let tre = try TextureResource.load(contentsOf: location, withName: "\(i).jpg")
                material.baseColor = try MaterialColorParameter.texture(tre)
                
                material.roughness = .float(0.0)
                
                (modelEntity as Entity & HasModel).model?.materials = [material]
            }catch{
                //print("no image", count)
            }
        }
    }
}

I want 25 boxes to transform into another model every ten seconds, the box, then the chess, then the box, then the chess

I am sure that parameter timeAccumulate is working, it is 0, then 1, then 0, then 1.

But the view does not change. It's always been a box.

What am I missing? Thank you.


Solution

  • Fix the following errors and it will do the trick:

    1. Use $ for timeAccumulate property wrapper in ContentView struct to get a binding struct.

      ARViewContainer(timeAccumulate: $timeAccumulate)
      
    2. Then use @Binding attribute for timeAccumulate property in ARViewContainer struct.

      @Binding var timeAccumulate: Int
      
    3. baseColor is still working in iOS 15 but it'll be irrelevant on iOS 16. So, use color instead:

      var material = SimpleMaterial()
      
      material.color = .init(tint: .white,
                          texture: .init(try! .load(named: "texture.png")))