24

I am having an issue with Landscape mode in my iPad application.

I created a very small new project to show my issue I set UIInterfaceOrientation in the pList to UIInterfaceOrientationLandscapeRight

In app delegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
    [self.window makeKeyAndVisible];
    MyController *myController = [[MyController alloc] init];
    [self.window addSubview:myController.view];

    return YES;
}

In MyController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    NSLog(@"Bounds Height:%f %f", self.view.bounds.size.height, self.view.bounds.size.width);
}

I have also tried putting this in viewDidLoad with same results

If I start the application while holding the device in landscape orientation the NSLog outputs

Bounds Height: 1004.000000 Bounds Width: 768.000000

What do I need to do to get the correct results? I am new to this iOS programming, all I am trying to do is anchor a UISlider to the bottom of the screen but when I am getting the incorrect coordinates I am unsure how to do it.

Kris
  • 6,094
  • 2
  • 31
  • 46
  • 1
    Are you specifically looking for bound ? bound and frame are different. http://stackoverflow.com/questions/1210047/iphone-development-whats-the-difference-between-the-frame-and-the-bounds – k-thorat Mar 04 '11 at 14:12
  • using both bounds or frame gives me the same result. – Kris Mar 04 '11 at 14:27
  • It's not a bug it's a feature. Bounds will always give you the same result. Because you know if you are in landscape or portrait mode you can still use the bounds for any calculation. It is confusing, but I think that is how it is supposed to be. –  Mar 04 '11 at 15:34
  • Have you implemented the usual shouldAutorotate method? Does the status bar appear at the top of the screen when you launch in landscape? – occulus Mar 04 '11 at 15:48
  • 3
    bresc, you're incorrect. See my answer. Did you mean "frame is always the same"? – occulus Mar 04 '11 at 16:38

9 Answers9

58

I dont think the original question was answered here because Im experiencing the same issue.

The key here is that if the simulator / device is in Landscape mode, and Then you start your program, self.view.(frame or bounds) retrieves the Portrait height and width. If your program is already running and then you rotate it gives the correct value. This only occurs when the device is started in Landscape and is not rotated.

Has anyone else found a solution to this or knows what Im doing wrong? Please & Thank you.

Possible Solution

I had previously been calling my method from the viewDidLoad method. It turns out that the view was still transitioning. I had better luck by calling my function from viewDidAppear that is called after everything is done.

Hope that helps.

RachelD
  • 4,072
  • 9
  • 40
  • 68
  • Thanks Rachel! I spent an hour trying to figure out what I was doing wrong. Then I stumbled upon your answer and everything works great now. You saved me! – Derek May 19 '12 at 22:25
  • ofc it was this simple. Thank you! – swe_mattias Jul 06 '12 at 11:40
  • 2
    ViewWillAppear instead of ViewDidLoad did it for me. Great stuff. – Jivko Petiov Mar 11 '13 at 15:55
  • Yup, I'm another one that made this mistake. Thanks for the tip! – Mike Gledhill Mar 14 '13 at 09:45
  • Thanks for the tip. Maybe it's good to edit your answer that the code can be placed inside "viewWillAppear"? I used first viewDidAppear like your suggestion, but in that case the user sees a transition from empty view to the specific view. If loading it in the "viewWillAppear" method I don't have this problem. – Matt Jun 04 '13 at 19:27
  • 5
    Try `viewDidLayoutSubviews` which is called after `viewWillAppear` and before `viewDidAppear` – Shizam Nov 01 '13 at 20:44
  • viewDidLayoutSubviews did the trick for me. As for my case I was getting 4inch device bounds in 3.5 inch simulator. – Nikolozi Mar 12 '14 at 01:44
  • @RachelD Could you please guide me with this issue in case of storyboard? I am using storyboard for all the views. – Piyush Dubey Mar 14 '14 at 13:13
12

You're checking the frame and bounds size too soon.

Instead, check them after rotation:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSLog(@"Bounds %@", NSStringFromCGRect(self.view.bounds));
    NSLog(@"Frame %@", NSStringFromCGRect(self.view.frame));
}

(Note my use of NSStringFromCGRect -- handy!)

This produces the output:

Bounds {{0, 0}, {1024, 748}}
Frame {{0, 0}, {748, 1024}}

So in this output the frame is 'wrong', but the bounds are what you expect. In fact, the frame isn't actually wrong, that's just how frame -> bounds calculations happen. So you need to access the bounds.

See also perhaps Do I have the right understanding of frames and bounds in UIKit?

N.B. viewDidAppear gets called sooner than you think in the scheme of things. According to Apple docs: "viewDidAppear notifies the view controller that its view was added to a window." In other words, it can happen before any rotation is applied.

Community
  • 1
  • 1
occulus
  • 16,959
  • 6
  • 53
  • 76
11

I had the same problem and hacked it like so:

- (CGSize)getRotatedViewSize
{
    BOOL isPortrait = UIInterfaceOrientationIsPortrait(self.interfaceOrientation);

    float max = MAX(self.view.bounds.size.width, self.view.bounds.size.height);
    float min = MIN(self.view.bounds.size.width, self.view.bounds.size.height);

    return (isPortrait ? 
            CGSizeMake(min, max) : 
            CGSizeMake(max, min));            
}
Sam
  • 3,659
  • 3
  • 36
  • 49
2

viewWillAppear (according to Paul Hegarty's CS193p lectures) is for geometry-related initializations. Since viewDidAppear follows viewWillAppear it makes sense that the bounds are correct here as well. The views bounds are not yet set in viewDidLoad.

Electro-Bunny
  • 1,380
  • 2
  • 12
  • 33
2

Apart from viewDidAppear: make sure using _window.rootViewController instead of [_window addSubview:_rootViewController.view]. That also solved my issues on iOS6.

AppDelegate.m:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
  _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  _rootViewController = [[MyRootViewController alloc] init];
  _window.rootViewController = _rootViewController;
  [_window makeKeyAndVisible];
  return YES;
}

MyRootViewController.m:

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];
  NSLog(@"bounds: %@", NSStringFromCGRect(self.view.bounds));
  UIView *myView = [[UIView alloc] initWithFrame:self.view.bounds];
  [self.view addSubview:myView];
}
basecode
  • 1,490
  • 12
  • 13
1

I created a small project to test frames and bounds because I was having the same problem. In fact checking for bounds on viewDidLoad resolved my issue.

I kept digging a little bit and I found out that the bounds are also correct on viewWillLayoutSubviews method. So I guess the most "correct" way to layout your views in a project would be:

  1. alloc and instantiate them on viewDidLoad or loadView with frames that you think it's correct by that time.

  2. re-layout your created views on viewWillLayoutSubviews so they are on the correct position when the view actually appear.

vfranchi
  • 478
  • 3
  • 13
1

One of the comments on the accepted answer contained the solution to the problem for me.

There is a method you can override on UIViewController called "viewDidLayoutSubviews", I tapped into it and adjusted my containing views from there.

And since its called after "viewWillAppear" and before "viewDidAppear" the changes you make to the subviews show up as expected, no glitches.

Mani
  • 1,597
  • 15
  • 19
  • I think this is the correct place to setup any sub views if you need them to appear correctly as the view appears. e.g. in transition animations between two controllers. – Ants Sep 25 '14 at 23:51
0

I had the same thing happen to me.

You have to spoon-feed iOS and put this line of code here or else it will give you the wrong answer.

-(void) viewWillAppear:(BOOL)animated
{
  [super viewWillAppear:animated];
  //required because device orientation will give you the wrong values
  [UIViewController attemptRotationToDeviceOrientation];
  int orientation = [[UIDevice currentDevice] orientation];
  BOOL isPortrait = false;

  if (orientation == 3 || orientation == 4)
    isPortrait = false;
  else
    isPortrait = true;
  NSLog(@"is portrait %i ?", isPortrait);
}
user1709076
  • 2,538
  • 9
  • 38
  • 59
0
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
CGRect r = self.view.frame;
CGFloat width = r.size.width;
CGFloat height = r.size.height;
r.size.height = (UIInterfaceOrientationIsLandscape(orientation)) ? MIN(width, height): MAX(width, height);
r.size.width  = (UIInterfaceOrientationIsLandscape(orientation)) ? MAX(width, height): MIN(width, height);
self.view.frame = r;
Durgesh
  • 1
  • 1