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]
        })
    }
}

Я загружаю их так, но когда открывается вид камеры и загружает одну модель, другие модели загружаться не будут. Кто-нибудь может мне помочь?


person Johanna    schedule 17.08.2020    source источник
comment
Прочтите этот пост, чтобы узнать, как это сделать: stackoverflow.com/questions/59618102/   -  person Andy Fedoroff    schedule 18.08.2020


Ответы (1)


Когда значение вашего Binding изменяется, SwiftUI вызывает вашу updateUIView(_:,context:) реализацию, которая не принимает во внимание.

Кроме того, вы не храните AnyCancellable. Когда токен, возвращенный sink, будет освобожден, запрос будет отменен. Это может привести к неожиданным сбоям при попытке загрузить модели лагеров.

Чтобы исправить обе эти проблемы, используйте 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)
    }
}

Мы создаем вложенный класс Coordinator, содержащий токен AnyCancellable, и перемещаем функцию loadModel в Coordinator. Помимо SwiftUI View, Coordinator - это class, который живет, пока ваш вид виден (всегда помните, что SwiftUI может создавать и уничтожать ваш View по желанию, его жизненный цикл не связан с фактическим видом, отображаемым на экране).

В классе out loadModel мы дважды проверяем, действительно ли значение нашего Binding изменилось, чтобы мы не отменяли текущий запрос для той же модели, когда SwiftUI обновляет наш View, например. из-за изменения окружающей среды.

Затем мы реализуем функцию makeCoordinator для создания одного из наших Coordinator объектов. И в makeUIView, и в updateUIView мы вызываем функцию loadModel на нашем Coordinator.

Метод dimantleUIView не является обязательным. Когда Coordinator будет деконструирован, наш token также будет выпущен, что заставит Combine отменить текущие запросы.

person jlsiewert    schedule 19.08.2020