Reputation: 485
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).
Upvotes: 2
Views: 484
Reputation: 17916
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];
}
Upvotes: 4