ARKit 会话暂停且未恢复

ARKit Session Paused and Not Resuming

在我的 ARKit 应用程序中,我展示了一个模式 window。当我关闭模态并返回 ARSCNView 时,我发现会话由于以下代码而暂停:

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

        // Pause the view's session
        sceneView.session.pause()
    } 

当我关闭模式并返回到 ARKit 相机视图屏幕时,将触发此代码:

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

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

        // Run the view's session
        sceneView.session.run(configuration)
    }

但是这段代码永远不会恢复会话。屏幕完全定格在它读到的最后一张图片上。有什么想法吗?

我将 viewDidAppear 代码更新为以下内容。它仍然卡在相机屏幕上,图像冻结。

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

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

        sceneView.session.delegate = self

        if self.isPaused {
            sceneView.session.run(sceneView.session.configuration!)
        } else {
            // Run the view's session
            sceneView.session.run(configuration)
        }


    }

不确定为什么你的会话没有恢复,但是......这通常不是你想要的情况。

Apple 的 ARKit 示例代码附带的自述文件中的注意事项(附于 WWDC17 session on ARKit):

Avoid interrupting the AR experience. If the user transitions to another fullscreen UI in your app, the AR view might not be an expected state when coming back.

Use the popover presentation (even on iPhone) for auxiliary view controllers to keep the user in the AR experience while adjusting settings or making a modal selection. In this example, the SettingsViewController and VirtualObjectSelectionViewController classes use popover presentation.

更详细一点:如果您暂停会话,当您的用户离开另一个全屏视图控制器时,它不会跟踪世界。这意味着当您恢复时,放置在场景中的任何虚拟内容都不会位于您离开它的位置(相对于相机)。

我知道你选择了一个答案,这个答案是apple推荐的,你可以重启AR Session。但是你不能 unpause/resume Session,因为一旦你离开你的控制器呈现 ARSceneView 设备就会停止跟踪,并且将停止跟踪你的设备相对于你放置的对象的位置现场。

无论如何,我基本上是通过销毁我的会话的所有方面并在我的视图重新出现时或通过按下按钮来重建它们来重新启动会话的。

我将在此处 post 一些示例代码。它在 Objective-C 中,因为我的项目是用它编写的,但它可能会帮助未来遇到同样问题的人。

-(void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated]
    [self setupScene];
    [self setupSession];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self destroySession];
    [self destroyScene];
}

- (void)setupScene {

// Setup the ARSCNViewDelegate - this gives us callbacks to handle new
// geometry creation
    self.sceneView.delegate = self;

// A dictionary of all the current planes being rendered in the scene
    self.planes = [NSMutableDictionary new];

// Contains a list of all the boxes rendered in the scene
    self.boxes = [NSMutableArray new];

// Show statistics such as fps and timing information
    self.sceneView.showsStatistics = YES;
    self.sceneView.autoenablesDefaultLighting = YES;

    SCNScene *scene = [SCNScene new];

    [self.sceneView setScene:scene];

    self.sceneView.scene.physicsWorld.contactDelegate = self;   
}

- (void)setupSession {

    // Create a session configuration
    ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new];
    //ARWorldTrackingSessionConfiguration *configuration = [ARWorldTrackingSessionConfiguration new]; This has been deprecated in favor of the previous line in XCode 9 beta 5. 

    // Specify that we do want to track horizontal planes. Setting this will cause the ARSCNViewDelegate
    // methods to be called when scenes are detected
    //configuration.planeDetection = ARPlaneDetectionHorizontal;

    // Run the view's session
    [self.sceneView.session runWithConfiguration:configuration options:ARSessionRunOptionResetTracking];
}


-(void)destroyScene {
    bottomPlane = nil;
    [self.sceneView setScene:nil];
    [self.sceneView setDebugOptions:nil];
    self.boxes = nil;
    self.planes = nil;
    self.sceneView.delegate = nil;  
}

-(void)destroySession {
    [self.sceneView.session pause];
    [self.sceneView setSession:nil];
}

这些销毁方法在视图消失时使用。我也在按下按钮时重新启动 AR Session,但不是通过这些方法。如下:

-(void)resetPressed{
    NSLog(@"Reset Pressed");
    [_sceneView.session pause];


    SCNScene *scene = [[SCNScene alloc] init];
    [_sceneView setScene:scene];
    [_sceneView.scene.rootNode enumerateChildNodesUsingBlock:^(SCNNode * _Nonnull child, BOOL * _Nonnull stop) {
        [child removeFromParentNode];
    }];

    ARWorldTrackingConfiguration *configuration = [[ARWorldTrackingSessionConfiguration ARWorldTrackingConfiguration] init];
    [_sceneView.session runWithConfiguration:configuration options:ARSessionRunOptionResetTracking | ARSessionRunOptionRemoveExistingAnchors];
}

希望对您有所帮助。

我不知道 iOS 11 GM Seed 或 XCode 9 GM Seed 版本今天是否修复了此问题,但是我可以使用原始问题中的代码成功恢复暂停的 ARSCNview。

sceneView.session.run(sceneView.session.configuration!)

这是使用 Swift 4.2 和 iOS 12 的答案。

要在您的 AR 场景上呈现在另一个视图控制器中定义的 UI,请创建您的视图控制器实例并将其 modalPresentationStyle 属性 设置为 .overCurrentContext:

EXAMPLE:

func showMaterialPicker(completion: (Texture?) -> Void) {

    // create an instance of your view controller, I have convenience functions
    // setup to do this via an extension on UIViewController
    guard let materialPicker = MaterialCategoriesViewController.instance(from: .product) else {
        print("Unable to instantiate MaterialCategoriesViewController, bailing")
        return
    }

    // set presentation style and transition style
    materialPicker.modalPresentationStyle = .overCurrentContext
    materialPicker.modalTransitionStyle = .crossDissolve

    // present the controller
    present(materialPicker, animated: true, completion: nil)
}

奖金提示:

要使叠加层看起来像抽屉一样从底部向上滑动,请设置

materialPicker.modalTransitionStyle = .coverVertical

然后将覆盖视图控制器中的视图从底部限制在舒适的高度,并将视图控制器视图的背景颜色设置为 UIColor.clear。

如果您想在显示叠加层时使 AR 视图变暗,您可以将背景颜色设置为黑色,opacity/alpha 值约为 0.75。

像这样:

self.view.backgroundColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.75)

或故事板中:

在上面的屏幕截图中,我将一个表视图固定到覆盖视图控制器视图的底部和侧面,高度限制为 300。

以这种方式完成后,您仍然可以看到覆盖视图控制器后面的 AR 视图,并且场景会继续渲染。