问题描述
我正在尝试使用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]
})
}
}
这是我加载它们的方式,但是当摄像机视图打开并加载一个模型时,将不会加载其他模型。有人可以帮我吗?
解决方法
当Binding
的值更改时,SwiftUI会调用您的updateUIView(_:,context:)
实现,但请注意。
此外,您没有存储AnyCancellable
。由sink
返回的令牌被释放后,请求将被取消。尝试加载更大的模型时可能会导致意外失败。
要解决这两个问题,请使用Coordinator
。
导入UIKit
导入RealityKit
导入SwiftUI
进口联合收割机
导入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
类,其中包含AnyCancellable
令牌,并将loadModel
函数移到Coordinator
中。
除了SwiftUI View
之外,Coordinator
是一个class
,它在您的视图可见时仍然存在(始终记住,SwiftUI可能会随意创建和销毁您的View
,生命周期与屏幕上显示的实际“视图”无关。
在loadModel
类的内部,我们再次检查Binding
的值是否实际更改,以便在SwiftUI更新View
时我们不会取消对同一模型的正在进行的请求,例如因为环境的变化。
然后,我们实现makeCoordinator
函数来构造我们的Coordinator
对象之一。
在makeUIView
和updateUIView
中,我们都在loadModel
上调用Coordinator
函数。
dimantleUIView
方法是可选的。当Coordinator
被解构时,我们的token
也将被释放,这将触发Combine取消正在进行的请求。