2

I have setup some views with the navigation controller. I want to be able to go back to the original view when I shake the phone. First, here's the code snippet which I have in my AppDelegate:

- (void)startAccelerometerUpdates
{

[motionManager startDeviceMotionUpdatesToQueue:queue withHandler:^(CMDeviceMotion *motion, NSError *error) {
    // based from 
    // http://stackoverflow.com/questions/5214197/simple-iphone-motion-detect/5220796#5220796
    float accelerationThreshold = 1; // or whatever is appropriate - play around with   different values
    CMAcceleration userAcceleration = motion.userAcceleration;
    if (fabs(userAcceleration.x) > accelerationThreshold || 
        fabs(userAcceleration.y) > accelerationThreshold || 
        fabs(userAcceleration.z) > accelerationThreshold) {



        if(!self->isRoot) {
            NSLog(@"Stopping motion manager");
            [motionManager stopDeviceMotionUpdates];
            NSLog(@"popping to top view controller");
            [navcon popToRootViewControllerAnimated:YES];
            NSLog(@"Done popping");
        }

    }

}];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   .....

   motionManager = [[CMMotionManager alloc] init];
   motionManager.deviceMotionUpdateInterval = .5;

   isRoot = malloc(sizeof(BOOL));
   self->isRoot = NO;
   [self startAccelerometerUpdates];

   [navcon pushViewController:main animated:NO];
   ....
}

I was expecting that the call to popToRoot would be immediate. However, it takes around 30 seconds. Interesting to note that the button on top that you could press to manually go back to the previous page doesn't work at this point. The phone seems to be doing a whole lot of work to freeze up the buttons. So, I'm definitely doing something wrong here.

It thought that maybe I keep calling the popToRootViewController several times so I added a check "isRoot" as you see in the code (please don't distracted on why BOOL is a pointer, I have a reason for that).

EDJ
  • 475
  • 1
  • 7
  • 16
  • Showing your actual log might be useful here. – tia Dec 05 '11 at 17:06
  • The log is simply those three lines... stopping, popping, done that I have in the code. The code runs through the method without problems. – EDJ Dec 05 '11 at 17:12

1 Answers1

4

Your motion handler block appears to be intended to run on a background queue. UIKit methods (like popToRootViewController) should only be called on the main thread, and the behavior when you fail to follow that rule is frequently similar to what you have described.

-performSelectorOnMainThread:withObject:waitUntilDone is the easiest way to make sure that your UIKit code runs on the main thread, but since -popToRootViewControllerAnimated: takes a non-object parameter, it takes a little more work. The simplest way is to add another method that takes no parameters:

-(void)popToRootView {
    [navcon popToRootViewControllerAnimated:YES];
}

and then update your block to call that method on the main thread:

if(!self->isRoot) {
    [motionManager stopDeviceMotionUpdates];
    [self performSelectorOnMainThread:@selector(popToRootView) withObject:nil waitUntilDone:NO];
}
Seamus Campbell
  • 17,816
  • 3
  • 52
  • 60
  • It gets called from the didFinishLaunchingWithOptions (I've updated my code). I'm not sure if that's a background thread or the main. Sorry, very new to IOS here :) – EDJ Dec 05 '11 at 17:11
  • The method `-startAcceleratorUpdates` is probably called on the main thread, but the block you define as a callback (the withHandler parameter to `-startDeviceMotionUpdatesToQueue:withHandler`) is almost certainly going to be called on a background thread from the queue you passed to the other parameter. – Seamus Campbell Dec 05 '11 at 17:17
  • Genius, genius! Thanks for the explanation and the solution. – EDJ Dec 05 '11 at 17:19