RealityKit – SwiftUI로 Reality Composer 장면로드

Aug 17 2020

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

이것이 내가 그들을로드하는 방법이지만 카메라 뷰가 열리고 하나의 모델을로드하면 다른 모델은로드되지 않습니다. 누군가 나를 도울 수 있습니까?

답변

jlsiewert Aug 19 2020 at 21:42

당신의 가치가 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을 보유 하는 중첩 클래스를 만들고 함수를 . SwiftUI 이외 의는 A는 보기가 표시되는 생활 동안 (항상 SwiftUI 작성하고이 파괴 수 있음을 기억한다는 뜻에서, 수명주기가 화면에 표시되는 실제 "보기"와 관련이 없습니다).AnyCancellableloadModelCoordinatorViewCoordinatorclassView

loadModel클래스 밖에서 Binding우리는 SwiftUI가를 업데이트 할 때 View, 예를 들어 환경의 변화로 인해 동일한 모델에 대한 지속적인 요청을 취소하지 않도록 우리의 값이 실제로 변경 되었는지 다시 확인 합니다.

그런 다음 객체 makeCoordinator중 하나를 생성 하는 함수를 구현 Coordinator합니다. 모두 makeUIView와에 updateUIView우리는 전화를 loadModel우리에 기능을 Coordinator.

dimantleUIView방법은 선택 사항입니다. 때 Coordinator해체됩니다 우리의 token지속적인 요청을 취소하기로 결합 트리거되는 잘으로 출시됩니다.