RealityKit – Carregando cenas do Reality Composer com SwiftUI
Estou tentando carregar modelos diferentes no rosto usando SwiftUI, RealityKit e 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]
})
}
}
É assim que eu os carrego, mas quando a visualização da câmera abre e carrega um modelo, os outros modelos não serão carregados. Alguém pode me ajudar?
Respostas
Quando o valor de suas Binding
alterações, SwiftUI está chamando sua updateUIView(_:,context:)
implementação, o que notando.
Além disso, você não está armazenando o arquivo AnyCancellable
. Quando o token retornado por sink
for desalocado, a solicitação será cancelada. Isso pode resultar em falhas inesperadas ao tentar carregar modelos maiores.
Para corrigir esses dois problemas, use um arquivo Coordinator
. 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)
}
}
Criamos uma Coordinator
classe aninhada que contém o AnyCancellable
token e movemos a loadModel
função para o arquivo Coordinator
. Além de SwiftUI View
, Coordinator
é um class
que vive enquanto sua visualização está visível (lembre-se sempre de que SwiftUI pode criar e destruir sua View
vontade, seu ciclo de vida não está relacionado à "visualização" real que é mostrada na tela).
Em loadModel
nossa classe, verificamos novamente se o valor de our Binding
realmente foi alterado para que não cancelemos uma solicitação em andamento para o mesmo modelo quando o SwiftUI atualizar nosso View
, por exemplo, devido a uma alteração no ambiente.
Em seguida, implementamos a makeCoordinator
função para construir um de nossos Coordinator
objetos. Tanto in makeUIView
quanto in updateUIView
chamamos a loadModel
função em nosso Coordinator
.
O dimantleUIView
método é opcional. Quando o Coordinator
for desconstruído, nosso token
também será liberado, o que fará com que o Combine cancele as solicitações em andamento.