当前位置 : 主页 > 手机开发 > ios >

ios – 带有ARSCNView的UIViewController在屏幕上呈现之前需要很长时间

来源:互联网 收集:自由互联 发布时间:2021-06-11
我有一个UIViewController,它使用ARSCNView并通过Scenekit向它添加一些元素,如下例所示.一切都工作正常,除非我打电话给当前显示这个视图控制器,它需要很长的时间或延迟才能在屏幕上显示.
我有一个UIViewController,它使用ARSCNView并通过Scenekit向它添加一些元素,如下例所示.一切都工作正常,除非我打电话给当前显示这个视图控制器,它需要很长的时间或延迟才能在屏幕上显示.

@IBOutlet var sceneView: ARSCNView!

override func viewDidLoad() {
    super.viewDidLoad()

    sceneView.showsStatistics =  DebugSettings.isDebugActive

    for (index, coach) in coachPositions.enumerated() {
        let coachGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.005)
        let coachNode = TrainEngineNode(position:  SCNVector3Make(0, Float(index) * 0.1, -0.5), geometry: coachGeometry)
        sceneView.scene.rootNode.addChildNode(coachNode)
    }

    self.sceneView.autoenablesDefaultLighting = true
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // Create a session configuration
    let configuration = ARWorldTrackingConfiguration()

    // Run the view's session
    sceneView.session.run(configuration)
}
正如大家所指出的那样,延迟是你在设置场景时阻塞主线程的事实.所有UI更新都发生在主线程上,并且显示刷新 60 times per second,除了可以做120Hz的最新iPad专业版.这意味着您所执行的任何同步工作单元必须在不到1/60 = 0.016667秒内完成.

您的初始化程序TrainEngineNode(position:geometry :)可能正在执行诸如从DAE或OBJ文件加载资源的工作,这可能是所有时间的所在.我这样说,因为如果你改变这一行:

let coachNode = TrainEngineNode(position:  SCNVector3Make(0, Float(index) * 0.1, -0.5), geometry: coachGeometry)

为此,我需要这样做,因为你的问题中没有提供TrainEngineNode:

let coachNode = SCNNode(geometry: coachGeometry)

那么iPhone 7没有明显的延迟.

SceneKit和ARKit不要求您对任何特定线程进行更改,因此只要您遇到这种情况,您就可以将工作卸载到后台队列,或者最好是您管理的串行队列.在适当的时间进行工作有一些注意事项,例如在ARSession完全运行后将对象添加到场景中.对于创建ARKit应用程序时可能遵循的模式的一些优秀想法,我推荐Apple提供的this sample.

简化示例如下:

class SimpleARViewController: UIViewController {

    @IBOutlet weak var sceneView: ARSCNView!
    weak var activityIndicator: UIActivityIndicatorView?

    var coachPositions = [1, 2, 3, 4, 5]

    let updateQueue = DispatchQueue(label: "com.example.apple-samplecode.arkitexample.serialSceneKitQueue")

    override func viewDidLoad() {
        super.viewDidLoad()

        sceneView.alpha = 0
        view.backgroundColor = .black
        sceneView.isPlaying = false
        sceneView.session.delegate = self

        let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
        activityIndicator.startAnimating()
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(activityIndicator)
        NSLayoutConstraint.activate([view.centerXAnchor.constraint(equalTo: activityIndicator.centerXAnchor, constant: 0),
                                     view.centerYAnchor.constraint(equalTo: activityIndicator.centerYAnchor, constant: 0)])
        self.activityIndicator = activityIndicator

        sceneView.autoenablesDefaultLighting = true
    }

    private func loadScene() {

        SCNTransaction.begin()
        SCNTransaction.disableActions = true

        for (index, _) in self.coachPositions.enumerated() {
            let coachGeometry = SCNBox(width: 0.1, height: 0.1, length: 0.1, chamferRadius: 0.005)

            // Simulates loading time of `TrainEngineNode(position:geometry:)`
            usleep(500000)

            let coachNode = SCNNode(geometry: coachGeometry)
            coachNode.worldPosition = SCNVector3Make(0, Float(index) * 0.1, -0.5)

            self.sceneView.scene.rootNode.addChildNode(coachNode)
        }

        SCNTransaction.commit()
        self.isLoading = false
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        let configuration = ARWorldTrackingConfiguration()
        sceneView.session.run(configuration)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        sceneView.session.pause()
    }

    var isLoading = true
    var hasLoaded = false
}

extension SimpleARViewController: ARSessionDelegate {

    func session(_ session: ARSession, didUpdate frame: ARFrame) {

        // Waiting until after the session starts prevents objects from jumping around
        if hasLoaded == false {
            hasLoaded = true

            updateQueue.async { [weak self] in
                self?.loadScene()
            }
        } else if isLoading == false {
            guard let activityIndicator = self.activityIndicator else { return }

            DispatchQueue.main.async {
                UIView.animate(withDuration: 0.35, animations: { [weak self] in
                    self?.sceneView.alpha = 1
                    activityIndicator.alpha = 0
                }, completion: { _ in
                    activityIndicator.removeFromSuperview()
                })
            }
        }
    }
}

此代码导致以下交互:

enter image description here

正如您在gif中看到的那样,视图控制器显示没有延迟,因为所有加载“工作”现在都在我们的专用串行队列上完成.通过将TrainEngineNode的创建与它们添加到场景的时间分离,可以进一步改善这个人为的例子.为了简洁起见,我保持逻辑与您已经拥有的逻辑类似,同时提供相对强大的初始化.对于3D对象加载的更强大/抽象的实现,我建议查看VirtualObjectLoader以及它在上述示例项目中的使用方式.

网友评论