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 Binding
modifications, 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 sink
est 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 Coordinator
qui contient le AnyCancellable
jeton et déplaçons la loadModel
fonction dans le fichier Coordinator
. Autre qu'un SwiftUI View
, Coordinator
c'est un class
qui 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 loadModel
notre classe, nous vérifions que la valeur de our Binding
a 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 makeCoordinator
fonction pour construire l'un de nos Coordinator
objets. Dans makeUIView
et dans updateUIView
nous appelons la loadModel
fonction sur notre Coordinator
.
La dimantleUIView
méthode est facultative. Lorsque le sera Coordinator
déconstruit, notre token
sera également publié, ce qui incitera Combine à annuler les demandes en cours.