UIScreen applicationFrame returning incorrect rectangle on landscape application launch (iPhone/iPad)
Asked Answered
R

8

21

Having trouble getting the correct bounds for my iPad application when launching it in landscape mode. I have the proper keys set in my Info.plist file, and my view controllers launch properly in landscape (and portrait, natch).

In my applicationDidFinishLaunching: method I'm calling a selector after a 3 second delay, and that method makes a call to [[UIScreen mainScreen] applicationFrame], but it's returning me a portrait frame (ie height > width).

Does anyone know how to fix this? It smells like a bug to me (if so I'll file a radar), but if it's intended behaviour, where is it documented?

Receptionist answered 19/4, 2010 at 2:58 Comment(1)
Can I just say "incorrectangle" from now on?Receptionist
O
6

When you are holding the iPad in landscape orientation and launch an app, the view controller initially sees bounds for a portrait view (even though orientation reports landscape). The view controller will then get a message to rotate to landscape orientation before it appears.

Of answered 19/4, 2010 at 20:59 Comment(1)
So after the message is sent to the view controller to rotate, will the coordinate system reflect the new orientation? For example, after the message is sent, width > height?Merilynmeringue
D
35

I never rely on [[UIScreen mainScreen] applicationFrame], especially during app launch.

When creating views in code, use the superview to set your frame.

If you're using xibs with "simulated interface elements" they will be correctly sized and everything will work great.

UINavigationController based apps

In the case of a UINavigationController based app, grab the frame directly from self.navigationController.view, don't try to use [self loadView] and self.view.superview. UINavigationController uses "hidden" subviews to do it's job--so the direct superview will not work.

UINavigationController is special because during app launch, the navigation controller resizes your views after loadView gets called. When autoresizing kicks in you end up with a small margin at the bottom of the screen.

Why not UIScreen

[[UIScreen mainScreen] applicationFrame] doesn't work reliably (especially during app launch in landscape). My experience is that the viewcontroller's interfaceOrientation property will not match the applicationFrame orientation.

Doolie answered 29/10, 2010 at 13:45 Comment(1)
This is very helpful! If I had found it earlier, I would had saved lot of time! Thanks!Bladder
A
14
CGRect bounds = [[UIScreen mainScreen] bounds]; // portrait bounds
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
    bounds.size = CGSizeMake(bounds.size.height, bounds.size.width);
}
Avan answered 11/2, 2013 at 9:52 Comment(0)
O
6

When you are holding the iPad in landscape orientation and launch an app, the view controller initially sees bounds for a portrait view (even though orientation reports landscape). The view controller will then get a message to rotate to landscape orientation before it appears.

Of answered 19/4, 2010 at 20:59 Comment(1)
So after the message is sent to the view controller to rotate, will the coordinate system reflect the new orientation? For example, after the message is sent, width > height?Merilynmeringue
B
6

This is the way I get the correct CGRect when the view controller is on landscape:

CGRect landscapeBounds;
if (self.view.frame.size.width < self.view.frame.size.height)
    landscapeBounds = CGRectMake(self.view.frame.origin.y, self.view.frame.origin.x, self.view.frame.size.height, self.view.frame.size.width);
else
    landscapeBounds = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
Boracic answered 31/1, 2012 at 23:50 Comment(0)
I
5

This is as designed. You should query the size of your superview and adjust as necessary.

Imbibition answered 19/4, 2010 at 4:22 Comment(5)
Could you explain why this is designed this way (or at least speculate). It seems illogical to me to give incorrect bounds for the screen if it has been rotated. The superview in question is my application's window (and I've tried using its frame as well to the same results).Receptionist
The reason is that windows can be rotated independently of the screen. Example: when a portrait application launches a YouTube video, the main window remains portrait, but a landscape window animates over top of it. During the animation, should the device be considered portrait or landscape? (accelerometer orientation is separate from window orientation is separate from statusbar orientation)Imbibition
I've noticed this as well, it's really weird. Thanks for the heads up.Angle
My application's window is my superview in this case, and it's reporting Portrait bounds even though it is landscape. Frustrating. But I'll just have to use [UIDevice orientation] to swap the w+h if needed.Receptionist
-[UIDevice orientation] should not be used for this purpose; -[UIApplication statusBarOrientation] should be used instead. UIDeviceOrientation has additional states that make no sense in this case: UIDeviceOrientationUnknown, UIDeviceOrientationFaceUp and UIDeviceOrientationFaceDown.Imbibition
F
2

[[UIScreen mainScreen] applicationFrame] will always return the portrait rectangle even if the app is in landscape mode. This is related to the fact that UIWindow never actually rotates, but just changes the transform of rootViewController.view instead.

To make sure, you can print the root view object in portrait and landscape modes, and you'll see something like this:

Portrait:

<UIView: 0x96290e0; frame = (0 20; 768 1004); ...

Landscape:

<UIView: 0x96290e0; frame = (0 20; 768 1004); transform = [0, 1, -1, 0, 0, 0]; ...
Foxhole answered 15/6, 2013 at 21:13 Comment(0)
L
1

So, add a launch image and give it the suffix -568h, according to Apple's guides.

I don't understand why anyone with a sound mind would make a system setting depend on a graphic; but I just tested and it worked.

Here is the spot that taught me after a quick search I didn't see this answer above, figured it'd be useful to someone.

S

Lashoh answered 15/6, 2013 at 20:20 Comment(0)
B
0

I got into same problem when dismissing view with dismissViewControllerAnimated in Cordova plugin. I modified singingAtom code for viewWillAppear method in MainViewController, which got resized after dismissing modal view:

- (void)viewWillAppear:(BOOL)animated
    {
    CGRect appFrame = [[UIScreen mainScreen] applicationFrame];
    UIDeviceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
    if (UIDeviceOrientationLandscapeLeft == orientation || 
        UIDeviceOrientationLandscapeRight == orientation)
            {
            if (appFrame.size.width < appFrame.size.height)
                {
                appFrame = CGRectMake(appFrame.origin.y, appFrame.origin.x, appFrame.size.height, appFrame.size.width);
                }
        }
    self.view.frame = appFrame;
    [super viewWillAppear:animated];
}
Bukovina answered 11/10, 2012 at 7:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.