RealityKit–;使用SwiftUI加载Reality Composer场景

RealityKit–;使用SwiftUI加载Reality Composer场景,swiftui,augmented-reality,arkit,realitykit,reality-composer,Swiftui,Augmented Reality,Arkit,Realitykit,Reality Composer,我正在尝试使用SwiftUI、RealityKit和ARKit在脸上加载不同的模型 struct AugmentedRealityView: UIViewRepresentable { @Binding var modelName: String func makeUIView(context: Context) -> ARView { let arView = ARView(frame: .zero) let con

我正在尝试使用SwiftUI、RealityKit和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]
        })
    }
}

这是我加载它们的方式,但当摄影机视图打开并加载一个模型时,其他模型将不会加载。有人能帮我吗?

当您的
绑定的值发生变化时,SwiftUI正在调用您的
UpdateUI视图(:,context:)
实现,但它没有任何作用

此外,您没有存储
anycancelable
。当
sink
返回的令牌被释放时,请求将被取消。当尝试加载较大的模型时,这可能会导致意外的失败

要解决这两个问题,请使用协调器

import UIKit
import RealityKit
import SwiftUI
import Combine
import 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)
    }
}
我们创建一个嵌套的
Coordinator
类,该类保存
anycancelable
标记,并将
loadModel
函数移动到
Coordinator
中。 除了SwiftUI
视图
协调器
是一个
,它在视图可见时存在(请记住,SwiftUI可能会随意创建和销毁视图
,其生命周期与屏幕上显示的实际“视图”无关)

在out
loadModel
类中,我们会仔细检查
绑定
的值是否已实际更改,以便在SwiftUI更新
视图时(例如,由于环境的变化),我们不会取消对同一模型的持续请求

然后我们实现
makeCoordinator
函数来构造一个
Coordinator
对象。 在
makeUIView
updateUIView
中,我们在
协调器上调用
loadModel
函数

import UIKit
import RealityKit
import SwiftUI
import Combine
import 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)
    }
}

dimantleUIView
方法是可选的。当协调人被解构时,我们的
令牌也会被释放,这将触发合并以取消正在进行的请求。

阅读本文了解如何执行此操作: