RealityKit: carga de escenas de Reality Composer con SwiftUI
Estoy tratando de cargar diferentes modelos en la cara usando SwiftUI, RealityKit y 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]
})
}
}
Así es como los cargo, pero cuando la vista de la cámara se abre y carga un modelo, los otros modelos no se cargarán. ¿Alguien me puede ayudar?
Respuestas
Cuando el valor de sus Binding
cambios, SwiftUI está llamando a su updateUIView(_:,context:)
implementación, que lo nota.
Además, no está almacenando el archivo AnyCancellable
. Cuando el token devuelto por sink
se desasigna, la solicitud se cancelará. Eso podría resultar en fallas inesperadas al intentar cargar modelos más grandes.
Para solucionar ambos problemas, use un archivo Coordinator
. importar UIKit importar RealityKit importar SwiftUI importar Combinar importar 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)
}
}
Creamos una Coordinator
clase anidada que contiene el AnyCancellable
token y movemos la loadModel
función al archivo Coordinator
. Aparte de SwiftUI View
, Coordinator
es una class
que vive mientras su vista está visible (siempre recuerde que SwiftUI puede crear y destruir su vista View
a voluntad, su ciclo de vida no está relacionado con la "vista" real que se muestra en la pantalla).
En loadModel
nuestra clase, verificamos dos veces que el valor de nuestro Binding
realmente cambió para que no cancelemos una solicitud en curso para el mismo modelo cuando SwiftUI actualice nuestro View
, por ejemplo, debido a un cambio en el entorno.
Luego implementamos la makeCoordinator
función para construir uno de nuestros Coordinator
objetos. Tanto en makeUIView
como en updateUIView
llamamos a la loadModel
función en nuestro Coordinator
.
El dimantleUIView
método es opcional. Cuando Coordinator
se deconstruye token
, también se libera, lo que provocará que Combine cancele las solicitudes en curso.