RealityKit - Memuat adegan Reality Composer dengan SwiftUI

Aug 17 2020

Saya mencoba memuat model yang berbeda di wajah menggunakan SwiftUI, RealityKit dan ARKit.

struct AugmentedRealityView: UIViewRepresentable {

    @Binding var modelName: String

    func makeUIView(context: Context) -> ARView {
    
        let arView = ARView(frame: .zero)
    
        let configuration = ARFaceTrackingConfiguration()

        arView.session.run(configuration, options: [.removeExistingAnchors, 
                                                    .resetTracking])
    
        loadModel(name: modelName, arView: arView)
    
        return arView
    
    }

    func updateUIView(_ uiView: ARView, context: Context) { }

    private func loadModel(name: String, arView: ARView) {

        var cancellable: AnyCancellable? = nil
    
        cancellable = ModelEntity.loadAsync(named: name).sink(
                 receiveCompletion: { loadCompletion in
            
            if case let .failure(error) = loadCompletion {
                print("Unable to load model: \(error.localizedDescription)")
            }                
            cancellable?.cancel()
        },
        receiveValue: { model in
            
            let faceAnchor = AnchorEntity(.face)
            arView.scene.addAnchor(faceAnchor)
            
            faceAnchor.addChild(model)
            
            model.scale = [1, 1, 1]
        })
    }
}

Beginilah cara saya memuatnya tetapi ketika tampilan kamera terbuka dan memuat satu model maka model lainnya tidak akan dimuat. Bisakah seseorang membantu saya?

Jawaban

jlsiewert Aug 19 2020 at 21:42

Ketika nilai Bindingperubahan Anda , SwiftUI memanggil updateUIView(_:,context:)implementasi Anda , yang tidak mencatat.

Selain itu, Anda tidak menyimpan file AnyCancellable. Ketika token yang dikembalikan oleh sinkdibatalkan alokasinya, permintaan akan dibatalkan. Itu bisa mengakibatkan kegagalan tak terduga saat mencoba memuat model lager.

Untuk memperbaiki kedua masalah ini, gunakan file Coordinator. impor UIKit impor RealityKit impor SwiftUI impor Gabungkan impor ARKit

struct AugmentedRealityView: UIViewRepresentable {
    class Coordinator {
        private var token: AnyCancellable?
        private var currentModelName: String?
        
        fileprivate func loadModel(_ name: String, into arView: ARView) {
            // Only load model if the name is different from the previous one
            guard name != currentModelName else {
                return
            }
            currentModelName = name
            
            // This is optional
            // When the token gets overwritten
            // the request gets cancelled
            // automatically
            token?.cancel()
            
            token = ModelEntity.loadAsync(named: name).sink(
                receiveCompletion: { loadCompletion in
                    
                    if case let .failure(error) = loadCompletion {
                        print("Unable to load model: \(error.localizedDescription)")
                    }
                },
                receiveValue: { model in
                    
                    let faceAnchor = AnchorEntity(.camera)
                    arView.scene.addAnchor(faceAnchor)
                    
                    faceAnchor.addChild(model)
                    
                    model.scale = [1, 1, 1]
                })
            }
        
        fileprivate func cancelRequest() {
            token?.cancel()
        }
    }
    
    @Binding var modelName: String
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    static func dismantleUIView(_ uiView: ARView, coordinator: Coordinator) {
        coordinator.cancelRequest()
    }
    
    func makeUIView(context: Context) -> ARView {
        
        let arView = ARView(frame: .zero)
        
        let configuration = ARFaceTrackingConfiguration()
        
        arView.session.run(configuration, options: [.removeExistingAnchors,
                                                    .resetTracking])
        
        context.coordinator.loadModel(modelName, into: arView)
        
        return arView
        
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {
        context.coordinator.loadModel(modelName, into: uiView)
    }
}

Kami membuat Coordinatorkelas bersarang yang menyimpan AnyCancellabletoken dan memindahkan loadModelfungsinya ke Coordinator. Selain SwiftUI View, Coordinatoris a classyang hidup saat tampilan Anda terlihat (selalu ingat bahwa SwiftUI mungkin membuat dan menghancurkan Viewsesuka Anda , siklus hidupnya tidak terkait dengan "tampilan" aktual yang ditampilkan di layar).

Di luar loadModelkelas kami memeriksa ulang bahwa nilai kami Bindingbenar - benar berubah sehingga kami tidak membatalkan permintaan yang sedang berlangsung untuk model yang sama ketika SwiftUI memperbarui kami View, misalnya karena perubahan lingkungan.

Kemudian kami mengimplementasikan makeCoordinatorfungsi untuk membangun salah satu Coordinatorobjek kami . Baik di dalam makeUIViewdan di dalam updateUIViewkami memanggil loadModelfungsi di kami Coordinator.

The dimantleUIViewmetode adalah opsional. Ketika Coordinatordidekonstruksi, kami juga tokenakan dirilis, yang akan memicu Gabungkan untuk membatalkan permintaan yang sedang berlangsung.