186

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:

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?

Wayne Chen
  • 305
  • 2
  • 15
lwxted
  • 2,419
  • 2
  • 16
  • 22

18 Answers18

174

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
memmons
  • 40,222
  • 21
  • 149
  • 183
vhristoskov
  • 2,074
  • 2
  • 16
  • 20
  • Is this in documentation anywhere? The docs shipped with beta 5 seem to have wrong information in them. – Hyperbole Aug 18 '14 at 17:03
  • What is the best way to detect the orientation within an app extension? Follow up question is here: http://stackoverflow.com/questions/25830448/what-is-the-best-way-to-detect-orientation-in-an-app-extension – barfoon Sep 14 '14 at 06:48
  • It's in the iOS 8 final documentation. – fabb Oct 02 '14 at 17:42
  • 7
    In the WWDC lecture mentioned above, the interface oriented part starts at 51:50. – cnotethegr8 Oct 28 '14 at 10:08
  • 3
    I've spent most of today trying to work around this advancement. Apparently it doesn't *always* happen, so if you're a developer of an SDK that needs to work in other applications, this stuff just got even more complicated. – Glenn Maynard Dec 15 '14 at 22:47
  • 1
    @aroth you can't innovate without braking some old habbits. – Boris Y. Jan 29 '15 at 12:10
  • 24
    @borisy - I respectfully disagree. In most cases it's possible innovate while preserving backwards compatibility. Breaking changes smack of laziness more than anything else, in my opinion. They want _everybody else_ to do the work. – aroth Jan 29 '15 at 12:34
  • 1
    This answer would be even more helpful if it was explained what _interface oriented_ actually means. I may be wrong, but I think the orientation dependence only pertains to view controllers. If you take any other class and calculate the bounds, they are still according to the old style, always portrait. – Maxi Mus Sep 14 '16 at 16:12
60

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;
}
Oscar Gomez
  • 18,436
  • 13
  • 85
  • 118
cbartel
  • 1,298
  • 1
  • 11
  • 18
  • 1
    I edited the code please check, your condition for checking OS version was wrong – Jageen Sep 01 '14 at 12:22
  • @Jageen Please clarify, the version check works for me. What is the problem? – cbartel Sep 02 '14 at 22:06
  • What i understand is, we need to change height and width if OS is iOS8 and orientation is Landscape,, am I right ? – Jageen Sep 03 '14 at 04:03
  • @Jageen Negative, in iOS 8 the method is orientation-dependent. Your logic is switched. Your edit was correctly rejected. – cbartel Sep 03 '14 at 21:05
  • @cbartel is there a way to categorize this into UIScreen so that our original call to [UIScreen mainScreen].bounds.size will work for both iOS 7 and iOS 8? – kevinl Sep 04 '14 at 22:23
  • How can this be checked in an application extension? Don't have access to `[UIApplication sharedApplication]` – barfoon Sep 14 '14 at 06:48
  • shouldn't it be >= NS...IOS_7_1 then? – Obiwahn Oct 12 '14 at 02:55
  • @obiwahn Negative, in iOS 8 the method is orientation-dependent. Your logic is switched. – cbartel Oct 13 '14 at 15:44
  • Is this really related to the iOS version, as your code suggest, or does it depend on the SDK version? – Hermann Klecker Jan 05 '15 at 20:24
35

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;
}
MaxK
  • 625
  • 7
  • 18
  • 2
    I'm finding that this fixes the problem with regular iPhone apps, but not with iPhone apps running on iPad. – ThomasW Aug 21 '14 at 01:11
34

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));
}
mnemia
  • 1,549
  • 2
  • 12
  • 14
  • 1
    Probably won't work on something like Apple TV though, where it's (almost) always wider than it is tall. – cbh2000 Nov 14 '15 at 08:02
11

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.

Antoine
  • 23,526
  • 11
  • 88
  • 94
  • @Namit Gupta, your edit is wrong. If the IOS version is 8 or above, we can use the UIScreen dimensions as these are orientation dependent. Before iOS 8 you had to check for the statusbar orientation. Your edit is now saying that all iOS versions not lower then 8 should check the statusbarorientation, which is wrong. – Antoine Oct 22 '14 at 11:15
10

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 {

}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
9

It is not a bug in iOS 8 SDK. They made bounds interface orientation depended. 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.

Julian
  • 9,299
  • 5
  • 48
  • 65
5

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
hfossli
  • 22,616
  • 10
  • 116
  • 130
  • 1
    Checking the SDK version as well is a good tip in case colleagues are building your app with differing versions of the SDK (though that shouldn't be happening!). – Craig McMahon Oct 09 '14 at 14:26
4

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;
}
Joe Booth
  • 501
  • 1
  • 4
  • 13
4

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;
}
Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
Uzair Khan
  • 251
  • 1
  • 2
  • 6
4

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]; 
2

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;
    }
}
nsinvocation
  • 7,559
  • 3
  • 41
  • 46
1

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
hhamm
  • 1,511
  • 15
  • 22
1

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
}
Martin Koles
  • 5,177
  • 8
  • 39
  • 59
1

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!

Sangram Shivankar
  • 3,535
  • 3
  • 26
  • 38
NoAngel
  • 1,072
  • 2
  • 18
  • 27
1

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.

Hardik Mamtora
  • 1,642
  • 17
  • 23
0

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.

epx
  • 1,066
  • 9
  • 17
0

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).

Mohammad Zaid Pathan
  • 16,304
  • 7
  • 99
  • 130
  • 1
    Question is not about device recognition based on the screen resolution. Moreover Apple recommends to create adaptive UI which is based on size classes not on device/screen detection – Julian Aug 07 '15 at 08:47