Is .bounds.size becoming orientation-dependent in iOS8?

I ran the following code in both iOS 7 and iOS 8:

UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
BOOL landscape = (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight);
NSLog(@"Currently landscape: %@, width: %.2f, height: %.2f", 
      (landscape ? @"Yes" : @"No"), 
      [[UIScreen mainScreen] bounds].size.width, 
      [[UIScreen mainScreen] bounds].size.height);

The following is the result from iOS 8:

  • Xcode could not find a valid private-key certificate pair for this profile in your keychain
  • How can a get the auto layout size of the UICollectionViewCells in iOS 8? (systemLayoutSizeFittingSize returns size with zero height in iOS 8)
  • Delivering MDM DeviceLock Payload
  • Object-c/iOS :How to use ASynchronous to get a data from URL?
  • Firebase Query not Executing Properly
  • Double tap Gesture on UICollectionViewCell?
  • Currently landscape: No, width: 320.00, height: 568.00
    Currently landscape: Yes, width: 568.00, height: 320.00
    

    Comparing to the result in iOS 7:

    Currently landscape: No, width: 320.00, height: 568.00
    Currently landscape: Yes, width: 320.00, height: 568.00
    

    Is there any documentation specifying this change? Or is it a temporary bug in iOS 8 APIs?

    19 Solutions Collect From Internet About “Is .bounds.size becoming orientation-dependent in iOS8?”

    Yes, it’s orientation-dependent in iOS8, not a bug. You could review session 214 from WWDC 2014 for more info: “View Controller Advancements in iOS 8”

    Quote from the presentation:

    UIScreen is now interface oriented:

    • [UIScreen bounds] now interface-oriented
    • [UIScreen applicationFrame] now interface-oriented
    • Status bar frame notifications are interface-oriented
    • Keyboard frame notifications are interface-oriented

    Yes, it’s orientation-dependent in iOS8.

    I wrote a Util method to resolve this issue for apps that need to support older versions of the OS.

    + (CGSize)screenSize {
        CGSize screenSize = [UIScreen mainScreen].bounds.size;
        if ((NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
            return CGSizeMake(screenSize.height, screenSize.width);
        }
        return screenSize;
    }
    

    Yes, indeed, screen size is now orientation dependent in iOS 8. Sometimes, however, it’s desired to get a size fixed to portrait orientation. Here is how I do it.

    + (CGRect)screenBoundsFixedToPortraitOrientation {
        UIScreen *screen = [UIScreen mainScreen];
    
        if ([screen respondsToSelector:@selector(fixedCoordinateSpace)]) {
                        return [screen.coordinateSpace convertRect:screen.bounds toCoordinateSpace:screen.fixedCoordinateSpace];
        } 
        return screen.bounds;
    }
    

    Yes, it’s now dependent on orientation.

    I prefer the below method of getting the screen size in an orientation-independent way to some of the answers above, both because it’s simpler and because it doesn’t depend on any of the orientation code (the state of which can be dependent on the time that they are called) or on version checking. You may want the new iOS 8 behavior, but this will work if you need it to be stable on all versions of iOS.

    +(CGSize)screenSizeOrientationIndependent {
         CGSize screenSize = [UIScreen mainScreen].bounds.size;
         return CGSizeMake(MIN(screenSize.width, screenSize.height), MAX(screenSize.width, screenSize.height));
    }
    

    Related to this question as it solved my problem, here two defines I use for screen width and height calculations:

    #define SCREEN_WIDTH (IOS_VERSION_LOWER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.width : [[UIScreen mainScreen] bounds].size.height) : [[UIScreen mainScreen] bounds].size.width)
    
    #define SCREEN_HEIGHT (IOS_VERSION_LOWER_THAN_8 ? (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation) ? [[UIScreen mainScreen] bounds].size.height : [[UIScreen mainScreen] bounds].size.width) : [[UIScreen mainScreen] bounds].size.height)
    
    #define IOS_VERSION_LOWER_THAN_8 (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    

    If you are supporting both iOS 7 and iOS 8, this is the best solution for this problem.

    It is not a bug in iOS 8 SDK. They made bounds interface oriented. According to your question about some reference or documentation to that fact I will highly recommend you to watch View Controller Advancements in iOS 8 it is 214 session from WWDC 2014. The most interesting part (according to your doubts) is Screen Coordinates which starts at 50:45.

    You can use nativeBounds (orientation-independent)

    nativeBounds

    The bounding rectangle of the physical screen, measured in pixels.
    (read-only)

    Declaration SWIFT

      var nativeBounds: CGRect { get }
    

    This rectangle is based on the device in a portrait-up orientation. This
    value does not change as the device rotates.

    Detecting the device’s height:

    if UIScreen.mainScreen().nativeBounds.height == 960.0 {
    
    }
    

    Detecting the device’s width:

    if UIScreen.mainScreen().nativeBounds.width == 640.0 {
    
    }
    

    Yes, it’s orientation-dependent in iOS8.

    Here’s how you can have a concistent way of reading out bounds in an iOS 8 fashion across SDK’s and OS-versions.

    #ifndef NSFoundationVersionNumber_iOS_7_1
    # define NSFoundationVersionNumber_iOS_7_1 1047.25
    #endif
    
    @implementation UIScreen (Legacy)
    
    // iOS 8 way of returning bounds for all SDK's and OS-versions
    - (CGRect)boundsRotatedWithStatusBar
    {
        static BOOL isNotRotatedBySystem;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            BOOL OSIsBelowIOS8 = [[[UIDevice currentDevice] systemVersion] floatValue] < 8.0;
            BOOL SDKIsBelowIOS8 = floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1;
            isNotRotatedBySystem = OSIsBelowIOS8 || SDKIsBelowIOS8;
        });
    
        BOOL needsToRotate = isNotRotatedBySystem && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation);
        if(needsToRotate)
        {
            CGRect screenBounds = [self bounds];
            CGRect bounds = screenBounds;
            bounds.size.width = screenBounds.size.height;
            bounds.size.height = screenBounds.size.width;
            return bounds;
        }
        else
        {
            return [self bounds];
        }
    }
    
    @end
    

    My solution is a combination of MaxK’s and hfossli. I made this method on a Category of UIScreen and it has no version checks (which is a bad practice):

    //Always return the iOS8 way - i.e. height is the real orientation dependent height
    + (CGRect)screenBoundsOrientationDependent {
        UIScreen *screen = [UIScreen mainScreen];
        CGRect screenRect;
        if (![screen respondsToSelector:@selector(fixedCoordinateSpace)] && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
            screenRect = CGRectMake(screen.bounds.origin.x, screen.bounds.origin.y, screen.bounds.size.height, screen.bounds.size.width);
        } else {
            screenRect = screen.bounds;
        }
    
        return screenRect;
    }
    

    I needed a quick helper function that kept the same behavior as iOS7 under iOS8 – this allowed me to swap out my [[UIScreen mainScreen] bounds] calls and not touch other code…

    + (CGRect)iOS7StyleScreenBounds {
        CGRect bounds = [UIScreen mainScreen].bounds;
        if (([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) && UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)) {
            bounds.size = CGSizeMake(bounds.size.height, bounds.size.width);
        }
            return bounds;
    }
    

    The below method can be used to find the screen bounds for a given orientation, independent of iOS version. This method will return the bounds based on the screen size of the device and will give the same CGRect value independent of iOS version.

    - (CGRect)boundsForOrientation:(UIInterfaceOrientation)orientation {
    
        CGFloat width   = [[UIScreen mainScreen] bounds].size.width;
        CGFloat height  = [[UIScreen mainScreen] bounds].size.height;
    
        CGRect bounds = CGRectZero;
    
        if (UIInterfaceOrientationIsLandscape(orientation)) {
            bounds.size = CGSizeMake(MAX(width, height), MIN(width, height));
        } else {
            bounds.size = CGSizeMake(MIN(width, height), MAX(width, height));
        }
    
        return bounds;
    }
    
    // For the below example, bounds will have the same value if you run the code on iOS 8.x or below versions.
    CGRect bounds = [self boundsForOrientation:UIInterfaceOrientationPortrait]; 
    

    self.window frame is now depends on the device orientation.

    That’s why we are facing the bounds.size orientation issue.

    To resolve this,
    http://iface2ios.wordpress.com/2014/10/02/bounds-size-landscape-orientation-issue-in-ios-8-and-working-fine-in-ios-7/

    Thats what I used to calculate the correct rect:

    UIScreen* const mainScreen = [UIScreen mainScreen];
    CGRect rect = [mainScreen bounds];
    #ifdef __IPHONE_8_0
    if ([mainScreen respondsToSelector:@selector(coordinateSpace)])
    {
        if ([mainScreen respondsToSelector:@selector(fixedCoordinateSpace)])
        {
            id tmpCoordSpace = [mainScreen coordinateSpace];
            id tmpFixedCoordSpace = [mainScreen fixedCoordinateSpace];
    
            if ([tmpCoordSpace respondsToSelector:@selector(convertRect:toCoordinateSpace:)])
            {
                rect = [tmpCoordSpace convertRect:rect toCoordinateSpace: tmpFixedCoordSpace];
            }
        }
    }
    #endif
    

    Just adding the swift version of an excellent cbartel function answered above.

    func screenSize() -> CGSize {
        let screenSize = UIScreen.mainScreen().bounds.size
        if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
            return CGSizeMake(screenSize.height, screenSize.width)
        }
        return screenSize
    }
    

    One thing that I noted is, the order of supported interface orientations in Info.plist does matter. I got the problem of this question with my app (that does orientation in code), but I did not specify anywhere that default orientation is Portrait.

    I thought that default orientation was Portrait in any case.

    Rearranging itens in Info.plist, putting Portrait first, restored the expected behavior.

    My issue was related to UIWindows frame which was going in minus.
    So made the code as below in MyViewController
    -(NSUInteger)supportedInterfaceOrientations Method

    [[UIApplication sharedApplication] setStatusBarHidden:NO];
    
    [self.view setFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height)];
    
    [appDel.window setFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height)];
    

    And its work for me try it.

    This will give correct device in iOS7 and iOS8 both,

    #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
    #define IS_PORTRAIT         UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation)
    
    + (BOOL)isIPHONE4{
    
    // < iOS 8.0
    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){
    
            if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 480.0) {
                return YES;
            } else {
                return NO;
            }
    
    // >= iOS 8.0
    }else{
    
        if(IS_PORTRAIT){
    
            if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 480.0) {
                return YES;
            } else {
                return NO;
            }
    
        }else{
    
            if ([self getDeviceWidth] == 480.0 && [self getDeviceHeight] == 320.0) {
                return YES;
            } else {
                return NO;
            }
    
        }
    
    }
    
    
    }
    
    + (BOOL)isIPHONE5{
    
    
    // < iOS 8.0
    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){
    
        if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 568.0) {
            return YES;
        } else {
            return NO;
        }
    
        // >= iOS 8.0
    }else{
    
        if(IS_PORTRAIT){
    
            if ([self getDeviceWidth] == 320.0 && [self getDeviceHeight] == 568.0) {
                return YES;
            } else {
                return NO;
            }
    
        }else{
    
            if ([self getDeviceWidth] == 568.0 && [self getDeviceHeight] == 320.0) {
                return YES;
            } else {
                return NO;
            }
    
        }
    
    }
    
    }
    
    + (BOOL)isIPHONE6{
    
    // < iOS 8.0
    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){
    
        if ([self getDeviceWidth] == 375.0 && [self getDeviceHeight] == 667.0) {
            return YES;
        } else {
            return NO;
        }
    
        // >= iOS 8.0
    }else{
    
        if(IS_PORTRAIT){
    
            if ([self getDeviceWidth] == 375.0 && [self getDeviceHeight] == 667.0) {
                return YES;
            } else {
                return NO;
            }
    
        }else{
    
            if ([self getDeviceWidth] == 667.0 && [self getDeviceHeight] == 375.0) {
                return YES;
            } else {
                return NO;
            }
    
        }
    
    }
    
    
    }
    + (BOOL)isIPHONE6Plus{
    
    
    // < iOS 8.0
    if(SYSTEM_VERSION_LESS_THAN(@"8.0")){
    
        if ([self getDeviceWidth] == 414.0 && [self getDeviceHeight] == 736.0) {
            return YES;
        } else {
            return NO;
        }
    
        // >= iOS 8.0
    }else{
    
        if(IS_PORTRAIT){
    
            if ([self getDeviceWidth] == 414.0 && [self getDeviceHeight] == 736.0) {
                return YES;
            } else {
                return NO;
            }
    
        }else{
    
            if ([self getDeviceWidth] == 736.0 && [self getDeviceHeight] == 414.0) {
                return YES;
            } else {
                return NO;
            }
    
        }
    
    }
    
    
    }
    
    + (CGFloat)getDeviceHeight{
    
    //NSLog(@"Device width: %f",[UIScreen mainScreen].bounds.size.height);
    return [UIScreen mainScreen].bounds.size.height;
    }
    + (CGFloat)getDeviceWidth{
    
    //NSLog(@"Device width: %f",[UIScreen mainScreen].bounds.size.height);
    return [UIScreen mainScreen].bounds.size.width;
    }
    

    //You may add more devices as well(i.e.iPad).

    iOS 8 or upper

    A solution for those who want to find out the screen size in points (3.5 inches screen has 320 × 480 points, 4.0 inches screen has 320 × 568 points, etc) would be

    - (CGSize)screenSizeInPoints
    {
        CGFloat width = [[UIScreen mainScreen] bounds].size.width;
        CGFloat height = [[UIScreen mainScreen] bounds].size.height;
    
        if (width > height) {
            return CGSizeMake(height, width);
        }
        else {
            return [[UIScreen mainScreen] bounds].size;
        }
    }
    

    Used slightly modified mnemia’s solution, that one without iOS version check, using min/max on mainscreen bounds.
    I needed a CGRect so got CGRect from mainscreen bounds and changed size.width=min(w,h), size.height=max(w,h). Then I called that OS-independent get CGRect function in two places in my code, where I getting screen size for OpenGL, touches etc. Before fix I had 2 problems – on IOS 8.x in landscape mode display position of OpenGL view was incorrect: 1/4 of full screen in left bottom part. And second touches returned invalid values. Both problems were fixed as explained. Thanks!