Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

shouldAutorotate behavior in iOS 8

I found a small behavior change between 7.1 and 8 on the UIViewController shouldAutorotate method. The Apple View Controller Programming Guide states that This method is called before performing any autorotation.

However I noticed that when I simply disable shouldAutorotate (return NO), the method is called on Portrait -> Landscape rotation, but not on the following Landscape -> Portrait rotation. Again the method should always be called as I understand it. This occurs when running on iOS 8, but not on iOS 7.1.

This seems related to a new method in the call stack in iOS8 :

[UIWindow shouldAutorotateToInterfaceOrientation:checkForDismissal:isRotationDisabled]

I could not find Is there any description of this method and its behavior, any idea where I could find more information about this internal method ?

Simple steps to reproduce this:

  1. Create a new Single View Application project in Xcode
  2. Update your ViewController.m to implement shouldAutorotate

    - (BOOL)shouldAutorotate
    {
        UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
        NSLog(@"%s orientation is %@", __PRETTY_FUNCTION__, [self stringFromDeviceOrientation:orientation]);
        // Disable autorotation of the interface.
        return NO;
    }
    
    - (NSString *) stringFromDeviceOrientation:(UIDeviceOrientation)uido
    {
        NSString *orientation = @"UIDeviceOrientationUnknown";
        switch (uido) {
            case UIDeviceOrientationPortrait:
                orientation = @"Portrait";
                break;
            case UIDeviceOrientationPortraitUpsideDown:
                orientation = @"PortraitUpsideDown";
                break;
            case UIDeviceOrientationLandscapeRight:
                orientation = @"LandscapeRight";
                break;
            case UIDeviceOrientationLandscapeLeft:
                orientation = @"LandscapeLeft";
                break;
            case UIDeviceOrientationFaceDown:
                orientation = @"FaceDown";
                break;
            case UIDeviceOrientationFaceUp:
                orientation = @"FaceUp";
                break; 
                default:
                    break;
            }
            return orientation;
        }
    
  3. Run on simulator iPhone 5s (7.1) : shouldAutorotate is called when switching to Landscape and back to Portrait

  4. Run on simulator iPhone 5s (8.0) or iPhone 6 : shouldAutorotate is called when switching to Landscape but it is not called when switching back to Portrait.
like image 985
lazi74 Avatar asked Oct 22 '14 08:10

lazi74


1 Answers

Same thing was happening to me. I have a feeling there is a bug in iOS8 because my same code worked fine in iOS7. Also just as a user I was have a lot of rotate problems in the apps I use. In 8 shouldAutorotate calls when you rotate to the side but then not again when you return to a normal rotation. That is unless it actually did an autorotation to the side, in which case on the return it is called.

So it's because your `shouldAutorotate' is always returning 'NO'. My assumption is that for some reason in iOS8 they (the apple coders who changed this behavior) decided that if they got a NO when rotating to the side, they don't need to call shouldAutorotate when rotating back to portrait. So it broke the behavior that we were expecting.

No one is talking about it or complaining. Google returns almost no results for this exact scenario. I think that's because in order for this to happen you have to be trying to use 'shouldAutorotate' as a device rotation notification but NOT actually be using the OS's auto rotation features. For example, I do opengl games. So I just this to let my engine know to rotate the game graphics. But I'm not rotating my view. I don't think many people were using this for device rotation notifications.

So I decided to use device notifications instead. In my view controller:

- (void) movingToBackground: (NSNotification *) notification {
    [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
}

- (void) movingToForeground: (NSNotification *) notification {
    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}

And to receive the messages:

   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewChanging:) name:UIDeviceOrientationDidChangeNotification object:nil];

And the method:

- (void)viewChanging:(NSNotification*)notification
{
   NSLog(@"View is changing");
   previousOrientation = orientation;
   orientation = [[UIDevice currentDevice] orientation];

   if (previousOrientation==orientation && forceReorientation==NO) {
       return;
   }
   ... do stuff ...

And then back in my view controller I turned all auto rotation stuff to NO.

like image 162
badweasel Avatar answered Nov 17 '22 16:11

badweasel