11

In my ARKit app I am presenting a modal window. When I close the modal and go back to the ARSCNView then I find out that the session is paused due to this code:

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

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

When I close the modal and go back to the ARKit camera view screen this code gets fired:

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

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

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

But this code never resumes the session. The screen is completely frozen on the last image it read. Any ideas?

I update the viewDidAppear code to be the following. It is still stuck on the camera screen with image frozen.

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


    }
john doe
  • 9,220
  • 23
  • 91
  • 167

5 Answers5

12

Not sure why your session isn't resuming, but... this generally isn't a situation you want to be in anyway.

Notice in the readme that ships with Apple's ARKit sample code (attached to the 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.

To go into a bit more detail: if you pause the session, it won't be tracking the world while your user is away in a different fullscreen view controller. That means that when you resume, any virtual content placed in the scene won't be in the positions (relative to the camera) where you left it.

Community
  • 1
  • 1
rickster
  • 124,678
  • 26
  • 272
  • 326
  • I'm seeing the session frozen even when using the popover presentation. Possible bug? https://forums.developer.apple.com/thread/81943 – Zach Lucas Jul 19 '17 at 19:42
  • I wonder if this advice may now be modified, given the advent of the ability to save and load world data? Am guessing this could potentially be saved prior to pause and reloaded upon resuming the session: https://developer.apple.com/documentation/arkit/data_management/saving_and_loading_world_data – stephent Aug 20 '21 at 00:03
5

I don't know if the iOS 11 GM Seed or XCode 9 GM Seed versions fixed this today however I can successfully resume a paused ARSCNview with code as in the original question.

sceneView.session.run(sceneView.session.configuration!)
wasabinz
  • 61
  • 1
  • 1
2

I get that you have chosen an answer, and that answer is what is recommended by apple, you can restart the AR Session. You can't unpause/resume the Session though, because the device stops it's tracking once you're out of your controller presenting the ARSceneView and will stop keeping track of the position of your device relative to the objects you've placed in the scene.

Anyway, I've managed to restart the session essentially by destroying all aspects of my session and rebuilding them them when my view reappears, or through a button press.

I'll post some sample code here. It's in Objective-C cause my project was written in that, but it might help future people with the same question.

-(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];
}

These destroy methods are used when the view disappears. I am also restarting the AR Session on a button press, but it is not through these methods. It is as follows:

-(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];
}

Hope it helps.

Alan
  • 1,132
  • 7
  • 15
  • This is what I have been doing. But somehow the second time around the ARSession is created if keeps missing frames for long periods of time. The camera state stays indefinitely at ARTrackingStateLimited. – nishant Jan 12 '18 at 02:50
  • I don't quite understand how are you addressing the change in the world centre coordinates. Most likely the world will not be with the same centre the second time you run the session. So all loaded nodes will be misplaced. Can you offer me a solution for this issue, because all I can come up with is some ImageRecognition software that can translate the change of world centres somehow, and give a translation matrix from there. Any better suggestions? – dvp.petrov Jan 12 '18 at 16:33
  • @dvp.petrov i am not addressing the change in coordinates. I was simply addressing the issue in the question, which was that when the OP returned to the AR View Controller the AR session was paused and doesn't move. I was simply offering a way on restarting the session once you go back to that view controller. With regards to your question, I guess you could save the positions of all the nodes you've added and re-add them once you're back, but there is no guarantee that the device will be in the same physical location at that point. – Alan Jan 15 '18 at 09:47
  • @AlanS - I am curious if the second time you run your ARSession it is as stable as the first time around? In my second run I have it forever as ARTrackingStateReasonInitializing and it never becomes ARTrackingStateReasonNone. – nishant Jan 18 '18 at 21:55
  • I am having the same issue on the 3rd revisit of the ARView controller. I call sceneView.session.pause() when I leave the view and .run when I return. I never unwind the view.. I just segue to a camera view to capture a picture. could this be the problem? – Westsider Sep 03 '21 at 21:01
1

Here's an answer working with Swift 4.2 and iOS 12.

To present UI defined in another view controller over your AR scene, create your view controller instance and set it's modalPresentationStyle property to .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)
}

Bonus tip:

To make your overlay appear to slide up from the bottom like a drawer, set

materialPicker.modalTransitionStyle = .coverVertical

then constrain your views in your overlay view controller a comfortable height from the bottom and set the background color of the view controllers view to UIColor.clear.

If you want to darken the AR view while your overlay is displayed you can set the background color to a black color with an opacity/alpha value of approximately 0.75.

Something like this:

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

or in the storyboard:

enter image description here

In the screenshot above I have a tableview pinned to the bottom and sides of the overlay view controllers view, and a height constraint of 300.

When done this way you can still see the AR view behind the overlay view controller and the scene continues to render.

digitalHound
  • 4,384
  • 27
  • 27
0

Inside viewDidLoad: create a tap event.

arKitView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:))))

Inside viewWillAppear:

arView.session.run(configuration)

And then define the tap event. In which first tap will pause the session and second tap will resume the session and so on.