I have an iPad-application. In landscape orientation the UIViewController's view actual width = 1024px and height = 768 - 20 (statusBar) - 44 (navigationBar) = 704px.

So I wanna get this [1024 x 704] size and I'm using self.view.bounds for it. It returns [748 x 1024], which is wrong! But when I rotate the screen twice (current -> portrait -> current), the view's bounds are correct - [1024 x 704].

The view was initialized like this:

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    self.view.backgroundColor = [UIColor lightGrayColor];
}

And bounds were get like this:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    NSLog(@"bounds = %@", NSStringFromCGRect(self.view.bounds));
}

So the question is.. How can I get the correct view's bound in the very beginning?

5 Answers 11

This is what I found in my last project: the frame of self.view will be adjusted after viewDidLoad according to if this screen has navigation bar etc.

So maybe you want to use that value after viewDidLoad (maybe in viewWillAppear or viewDidAppear) or adjust it manually by substract the height of bars.

upvote
  flag
I'm already using it in viewDidLoad method and after it. And bounds stay wrong until I rotate the screen. Manual subtracting is bad solution, because application is targeted for all devices & orientations.. – demon9733
3 upvote
  flag
Will did you using it after viewDidLoad? In my case, viewDidAppear will return right size. – Selkie

I've always used:

CGSize sizeOfScreen = [[UIScreen mainScreen] bounds].size;

to get the size of the screen, and:

CGSize sizeOfView = self.view.bounds.size;

to get the view's size. I have just tested it on viewDidLoad and it returned:

2012-07-17 10:25:46.562 Project[3904:15203] bounds = {768, 1004}

Which is correct since the CGSize is defined as {Width, Height}.

1 upvote
  flag
Yes, it works only after rotating :( – demon9733
upvote
  flag
That's odd. The fact that you are getting the size in viewDidLoad shouldn't matter because the view "is already loaded" – Luke

I ran into this issue and found that getting the bounds in viewDidAppear worked for my needs.

up vote 29 down vote accepted

As per some of the other answers, the issue you are seeing is because viewDidLoad is called before the rotation happens. Because the iPad always initializes in portrait mode, if you get the size values in viewDidLoad, they will always be the portrait sizes - this is irrespective of any orientations you've configured.

To get the size after the orientation/rotation happens, get the size values in viewDidAppear.


I don't particularly understand why iOS doesn't handle this better - especially given that you define the orientations in the project settings, and, in Xcode Interface Builder. But, I'm sure there is a good reason ;-).

1 upvote
  flag
If you are creating heavy views ,there can be delay in view appearance , you can reduce the delay by using viewWillAppear – Dinesh Balu
upvote
  flag
I tried viewWillAppear. It does not work as desired. Seems that the bounds calculation is done after the view has appeared. – Billy
1 upvote
  flag
I strongly recommend not to use viewDidAppear. Your view is on screen and visible to the user by this point and changes you make will can cause abrupt and visible glitches. It also won't help you respond to changes of view size during rotation (etc). Instead, use viewWillLayoutSubviews (disclosure: links to my answer) which is the way iOS handlers this. – Benjohn
2 upvote
  flag
viewDidAppear won't work if user make a rotation after it did appear. correct answer is from @Benjohn viewWillLayoutSubviews will called everytimes when you either rotate it or not – Einzeln

How to do this correctly.

Your UIViewController subclass should override the method viewWillLayoutSubviews, see also here.

When this method is called, the viewController's view has its correct size and you can make any necessary adjustments to subviews prior to the layout pass over the subviews.

Documentation.

When a view's bounds change, the view adjusts the position of its subviews. Your view controller can override this method to make changes before the view lays out its subviews. The default implementation of this method does nothing.

As you see from the emphasised part, this method is called every time the view controller's view changes size, as well as when the view first appears. This lets you respond correctly to rotation and other bounds change events

Some wrong ways to do this.

A couple of methods suggested by other answers here do not work well are completely broken. You really shouldn't use these methods. Here they are, with why you should avoid them. Please.

  • viewDidLoad and viewWillAppear – during these calls view does not yet have its final size. The size you get will only ever be correct by pure chance, so as to mislead you. Please, I beg you, don't use these methods.
  • viewDidAppear – this is too late. Your view is already on screen and visible to the user. Making changes here will cause visible changes / abrupt glitches and will look like terrible amateur hour rubbish. Once again, please – for your sake, for my sake, for everyone's sake: don't do it! You're better than that and so are your users.

Other than not providing correct information, these broken approaches will not help you with auto-rotation or other events that cause the view controller's view to change size such as multitasking gestures. This is something that you really want to handle smoothly.

So please: be a champ. Do it the right way. Use viewWillLayoutSubviews. Your implementation will be called for every size change, and your users, future self, team members and I will celebrate you for it. Bravo!

Other Tips.

When viewWillLayoutSubviews is called, the only view in your hierarchy that will be resized to its final size is viewController.view. The give away for this is in the name of the method. It's telling you view… (your view controller's view) …WillLayout… (really soon now, but it's not happened yet) …Subviews (everything else in its hierarchy under the root view).

But it's not happened yet. So every single child under the root does not yet have a valid final size. Any size information you query from the child views will be at best completely wrong.

Rather more likely, and infinitely worse, it will be misleadingly correct!

It happens to be what you expect and need due to a default at this size and orientation, or due to your storyboard settings. But this is only by chance and isn't something you can rely on with different device sizes or orientations.

If you need to be told when a particular subview changes size, and know its exact final size, you should override the layoutSubviews method of that particular UIView subclass.

upvote
  flag
Thanks for the answer! To clarify, are you saying if we want to know the size of a subview that is of class UIView, the only way to properly do so is to override its layoutSubviews method (which would require creating a custom class)? – Crashalot
upvote
  flag
@Crashalot Overriding makes sense for a subclass that lays out its children. However, it can be problematic if you often want to compose this behaviour rather than override an existing view class. An approach I've taken is to create a specific UIView subclass, BoundsSenderView, that only has the behaviour of forwarding layoutSubviews to some injected boundsListener handler. It doesn't display anything or respond to touch, etc. You can then use auto layout to pin it on to the view that you want the size of, and you'll get the callback when this changes size. – Benjohn
upvote
  flag
There might be other ways to get the size of a subview after layout, but I'm not aware of them. You'd need to hook in to the complete layout pass having finished. – Benjohn
upvote
  flag
Thanks for the response. It's so frustrating that Apple makes this difficult. – Crashalot

Not the answer you're looking for? Browse other questions tagged or ask your own question.