RealityKit - загрузка сцен из Reality Composer с помощью SwiftUI
Я пытаюсь загрузить разные модели на лицо с помощью 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объектов. Как in, так makeUIViewи in updateUIViewмы вызываем loadModelфункцию в нашем Coordinator.
dimantleUIViewМетод является необязательным. Когда Coordinatorобъект будет деконструирован, он также tokenбудет выпущен, что приведет к тому, что Combine будет отменять текущие запросы.