Waiting for UIViewController animation before animation completes
I use this observer: UIDeviceOrientationDidChangeNotification
to detect when the user changes device orientation. When the orientation has changed to a landscape, I present a new one UIViewController
or reject this UIViewController
one when it changes to a portrait.
My problem starts when the user starts to rotate the number of devices times and quickly and then the app goes crazy until I get this error:
Warning. Try to imagine whose view is not in the window hierarchy! `.
What's the best way to wait for the animation to finish and then change the rotation?
This is what I am using in the view of the view controller :
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self beginDeviceOrientationListener];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)beginDeviceOrientationListener
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
}
- (void)orientationChanged:(NSNotification *)notification
{
UIDevice *device = notification.object;
switch (device.orientation)
{
case UIDeviceOrientationLandscapeLeft:
case UIDeviceOrientationLandscapeRight:
{
TheViewControllerToPresent *viewController = [[TheViewControllerToPresent alloc] init];
[self presentViewController:viewController animated:YES completion:nil];
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationLandscapeRight] forKey:@"orientation"];
[[UIApplication sharedApplication] setStatusBarOrientation:[[[UIDevice currentDevice] valueForKey:@"orientation"] integerValue] animated:YES];
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
}
break;
default:
break;
}
}
This is what I am using in the Presented View Controller :
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self beginDeviceOrientationListener];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)beginDeviceOrientationListener
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:[UIDevice currentDevice]];
}
- (void)orientationChanged:(NSNotification *)notification
{
UIDevice *device = notification.object;
switch (device.orientation)
{
case UIDeviceOrientationPortrait:
{
[self dismissViewControllerAnimated:YES completion:nil];
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger:UIInterfaceOrientationPortrait] forKey:@"orientation"];
[[UIApplication sharedApplication] setStatusBarOrientation:[[[UIDevice currentDevice] valueForKey:@"orientation"] integerValue] animated:YES];
}
break;
default:
break;
}
}
source to share
The simplest solution I use myself is to stop the user from making any changes during animation.
This is done by adding the following code when the animation starts:
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
and the completion handler:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
The user can rotate the device, but no events will be generated during animation, so animations will not collide. However, when the animation ends, you must explicitly check the orientation:
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
to see if another animation should run or not.
source to share