RealityKit - Chargement de scènes Reality Composer avec SwiftUI
J'essaie de charger différents modèles sur le visage à l'aide de SwiftUI, RealityKit et 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]
})
}
}
C'est ainsi que je les charge, mais lorsque la vue de la caméra s'ouvre et charge un modèle, les autres modèles ne seront pas chargés. Est-ce que quelqu'un peut m'aider?
Réponses
Lorsque la valeur de vos Bindingmodifications, SwiftUI appelle votre updateUIView(_:,context:)implémentation, ce qui le note.
De plus, vous ne stockez pas le fichier AnyCancellable. Lorsque le jeton renvoyé par sinkest désalloué, la demande est annulée. Cela pourrait entraîner des échecs inattendus lors de la tentative de chargement de modèles de bière blonde.
Pour résoudre ces deux problèmes, utilisez un fichier Coordinator. importer UIKit importer RealityKit importer SwiftUI importer Combiner importer 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)
}
}
Nous créons une classe imbriquée Coordinatorqui contient le AnyCancellablejeton et déplaçons la loadModelfonction dans le fichier Coordinator. Autre qu'un SwiftUI View, Coordinatorc'est un classqui vit tant que votre vue est visible (n'oubliez jamais que SwiftUI peut créer et détruire votre Viewà volonté, son cycle de vie n'est pas lié à la "vue" réelle qui s'affiche à l'écran).
Dans loadModelnotre classe, nous vérifions que la valeur de our Bindinga réellement changé afin de ne pas annuler une demande en cours pour le même modèle lorsque SwiftUI met à jour notre View, par exemple en raison d'un changement d'environnement.
Ensuite, nous implémentons la makeCoordinatorfonction pour construire l'un de nos Coordinatorobjets. Dans makeUIViewet dans updateUIViewnous appelons la loadModelfonction sur notre Coordinator.
La dimantleUIViewméthode est facultative. Lorsque le sera Coordinatordéconstruit, notre tokensera également publié, ce qui incitera Combine à annuler les demandes en cours.