Is [UIScreen mainScreen].bounds.size becoming orientation-dependent in iOS8?
Asked Answered
S

18

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?

Stepp answered 10/6, 2014 at 20:35 Comment(1)
Well the iOS8 output seems much more logicalKinchinjunga
B
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
Brickwork answered 11/6, 2014 at 1:59 Comment(8)
Is this in documentation anywhere? The docs shipped with beta 5 seem to have wrong information in them.Petrol
What is the best way to detect the orientation within an app extension? Follow up question is here: #25830948Entry
It's in the iOS 8 final documentation.Backwash
In the WWDC lecture mentioned above, the interface oriented part starts at 51:50.Fy
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.Deaver
@aroth you can't innovate without braking some old habbits.Dermal
@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.Occurrence
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.Haleakala
B
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;
}
Binnings answered 1/8, 2014 at 20:50 Comment(9)
I edited the code please check, your condition for checking OS version was wrongExcitable
@Excitable Please clarify, the version check works for me. What is the problem?Binnings
What i understand is, we need to change height and width if OS is iOS8 and orientation is Landscape,, am I right ?Excitable
@Excitable Negative, in iOS 8 the method is orientation-dependent. Your logic is switched. Your edit was correctly rejected.Binnings
@Binnings 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?Annetteannex
How can this be checked in an application extension? Don't have access to [UIApplication sharedApplication]Entry
shouldn't it be >= NS...IOS_7_1 then?Distinguish
@obiwahn Negative, in iOS 8 the method is orientation-dependent. Your logic is switched.Binnings
Is this really related to the iOS version, as your code suggest, or does it depend on the SDK version?Romance
P
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;
}
Pocky answered 8/8, 2014 at 18:11 Comment(1)
I'm finding that this fixes the problem with regular iPhone apps, but not with iPhone apps running on iPad.Inwardness
A
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));
}
Acumen answered 23/9, 2014 at 18:48 Comment(1)
Probably won't work on something like Apple TV though, where it's (almost) always wider than it is tall.Dobby
W
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.

Wrongful answered 18/10, 2014 at 13:52 Comment(1)
@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.Wrongful
Y
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 {

}
Yautia answered 4/1, 2015 at 0:20 Comment(3)
Be aware that this method returns pixels, while most other methods deal with points. Very different things.Plague
In my case exactly what I needed :) (OpenGL or other Views not based on UIViews but where I need to know the exact pixel dimensions)Englishism
Warning: on iphone 6+ this returned 1080 x 1920 which might not be what you wantOffbeat
P
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.

Pomeroy answered 5/8, 2014 at 13:41 Comment(0)
S
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
Slingshot answered 11/9, 2014 at 15:49 Comment(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!).Demarcusdemaria
J
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;
}
Johm answered 8/10, 2014 at 5:32 Comment(1)
Nasty, but gets the job done until time can be put into fixing things properly :-)Olympie
R
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;
}
Rate answered 14/10, 2014 at 21:9 Comment(0)
H
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]; 
Heall answered 11/11, 2014 at 6:17 Comment(0)
E
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;
    }
}
Excellent answered 3/12, 2015 at 18:32 Comment(0)
N
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
Norvil answered 28/10, 2014 at 9:29 Comment(0)
W
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
}
Whitten answered 3/1, 2015 at 22:11 Comment(0)
N
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!

Notions answered 26/1, 2015 at 7:34 Comment(0)
A
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.

Adamic answered 29/1, 2015 at 13:17 Comment(0)
P
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.

Puseyism answered 26/9, 2014 at 19:35 Comment(0)
I
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).

Ironhanded answered 23/3, 2015 at 9:33 Comment(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 detectionPomeroy

© 2022 - 2024 — McMap. All rights reserved.