我正在try 创建一个函数来暂停AR会话,同时清除我的字典变量entityDictionaries并删除所有锚点.然而,我有点被困住了,不知道如何开始.

以下是我的主视图的代码:

    struct InitiateARView: View {
    @StateObject var vm = ARPreparationViewModel()
     
    var body: some View {
        VStack {
             if vm.isLoading {
                 Button("Start", action: {
                     vm.prepareAR()
                 })
                 
             } else {
                 ZStack {
                     ARExperience()
                         .edgesIgnoringSafeArea(.all)
                     VStack {
                         HStack {
                             Spacer()
                             Button("Stop AR", action: {
                                // Call to clear AR session function here
                             })
                         }
                         Spacer()
                     }
                 }
             }  
         }
     }
}

下面是我的ARExperience struct 的简化版本

struct ARExperience: UIViewRepresentable {
    var arView = ARView(frame: .zero) 
    
    var entityDictionaries = [String: (anchor: AnchorEntity, model: ModelEntity)]()
    
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    
    class Coordinator: NSObject, ARSessionDelegate {
        var parent: ARExperience
    
        init(parent: ARExperience) {
            self.parent = parent
        }
        
        func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
            guard let validAnchor = anchors[0] as? ARImageAnchor else { return }
            let anchor = AnchorEntity(anchor: validAnchor)
            let videoPlane = createdVideoPlayerNodeFor(validAnchor.referenceImage)
            anchor.addChild(videoPlane)
            parent.arView.scene.addAnchor(anchor)
            if let targetName = validAnchor.referenceImage.name {
                parent.entityDictionaries[targetName] = (anchor, videoPlane)
            }
        }

        func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {

        }
        
    }

    func makeUIView(context: Context) -> ARView {
        //...        
    }
    
    func updateUIView(_ uiView: ARView, context: Context) {

    }
}

任何能让我摆脱这个问题的帮助都将是完美的

推荐答案

要做到这一点,一种方法是将UIKit从SwiftUI中分离出来,并拥有一个共同的真理来源,在那里这两个框架可以相互通信.

struct InitiateARView: View {
    @StateObject var vm = ARPreparationViewModel()
    
    var body: some View {
        VStack {
            if vm.isLoading {
                Button("Start", action: {
                    vm.prepareAR()
                })
                
            } else {
                ZStack {
                    ARExperience_UI(vm: vm) //Your experiece would change to include the shared VM
                        .edgesIgnoringSafeArea(.all)
                    VStack {
                        HStack {
                            Spacer()
                            Button("Stop AR", action: {
                                //Make SwiftUI changes
                                vm.clearAR() // Then you can access UIKit via the VM
                            })
                        }
                        Spacer()
                    }
                }
            }
        }
    }
}

共享的真相来源应该是这样的.

//Works with both SwiftUI and UIKit
class ARPreparationViewModel: ObservableObject{
    //reference to UIKit, will be nil until set by ViewController init.
    @Published var vc: ARExperienceVC?
    @Published var isLoading: Bool = false
    
    deinit {
        vc = nil
    }
    
    func prepareAR() {
        guard let vc = vc else {
            print("Missing View Controller")
            return
        }
        vc.prepareAR()
    }
    func clearAR() {
        guard let vc = vc else {
            print("Missing View Controller")
            return
        }
        //Make any source of truth changes
        vc.clearAR()
    }
}

然后,UIViewControllerRepresentable代码将被简化为只创建新的UIViewController,这只是一个桥梁.

// SwiftUI
struct ARExperience_UI: UIViewControllerRepresentable { //Change to a UIViewControllerRepresentable
    let vm: ARPreparationViewModel
    
    func makeUIViewController(context: Context) -> some UIViewController {
        ARExperienceVC(vm: vm) // Create the new ViewController with all the UIKit Code
    }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        
    }
}

UIKit代码执行繁重的任务.

// Keep UIKit code here
class ARExperienceVC: UIViewController {
    let vm: ARPreparationViewModel

    lazy var arView: ARView = {
        let arView = ARView(frame: .zero)
        arView.session.delegate = self
        
        return arView
    }()
    
    init(vm: ARPreparationViewModel) {
        self.vm = vm
        super.init(nibName: nil, bundle: nil)
        self.vm.vc = self //Without this the source of truth won't have access to the ViewController
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    deinit {
        self.vm.vc = nil
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(arView)
        arView.pinToSuperview(self.view) // This lest SwiftUI control the size because the view will be attached to the bordering anchors.
    }
    
    func prepareAR() {
        print("\(type(of: self)) :: \(#function)")
    }
    func clearAR() {
        print("\(type(of: self)) :: \(#function)")
        //Make UIKit changes.
    }
}

extension ARExperienceVC: ARSessionDelegate {
    func session(_ session: ARSession, didAdd anchors: [ARAnchor]) {
        
    }

    func session(_ session: ARSession, didUpdate anchors: [ARAnchor]) {

    }
}

extension UIView{
    func pinToSuperview(_ superView: UIView){
        self.translatesAutoresizingMaskIntoConstraints = false
        self.topAnchor.constraint(equalTo: superView.topAnchor).isActive = true
        self.bottomAnchor.constraint(equalTo: superView.bottomAnchor).isActive = true
        self.leadingAnchor.constraint(equalTo: superView.leadingAnchor).isActive = true
        self.trailingAnchor.constraint(equalTo: superView.trailingAnchor).isActive = true
    }
}

Swift相关问答推荐

如何在Swift 中创建移动的uil标签?

解析SON数据的API调用有什么问题?

如何更新Square Order?

在运行时检测蒸汽工人的类型

如何在向照片添加文本时定义文本对齐、文本背景的Alpha和在文本之前添加图像

按下一步后无法让文本字段切换焦点

在MacOS上检测键盘是否有Globe键

SwiftUI 中的同一个 ForEach 中是否可以有 2 个数组?

从 Obj-C 函数返回 swift 类的不兼容指针类型

覆盖一个子元素的 HStack 对齐方式

swiftui中服务端图片如何设计view并赋予rotationEffect?

有没有更快的方法来循环浏览 macOS 上已安装的应用程序?

为什么 id 不能通过 struct 从 Objective-C 移植到 Swift?

Vapor 4 身份验证问题 [用户未通过身份验证]

在 SwiftUI 中,如何在 UIView 内或作为 UIView 使用 UIHostingController?

在 Xcode 中自动实现 Swift 协议方法

Swift 2 中的新 @convention(c):我该如何使用它?

CMutablePointer - 如何访问它?

使用相机进行人脸检测

iOS 视图可见性消失了