I am trying to execute following code in viewDidLoad method of my single view controller project:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
But it does not give the desired inset. However other UI changes i make in the same method do work e.g
self.view.layer.backgroundColor = [UIColor orangeColor].CGColor;
Above line of code does work and background is change to orange but the frame does not.
The inset works only if I place the line of code in viewDidAppear. I would like to understand the key reason for this behavior if anyone can explain. Thank you in advance.
I think the issue you are running into with this line:
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
can be explained like so :
You can find the complete explanation of this in this season's Stanford CS193P course about iOS programming (very cool by the way).
So if you want it to work just once, use :
- (void)viewDidLayoutSubviews
{
self.view.layer.frame = CGRectInset(self.view.layer.frame, 20, 20);
}
PS : I posted this answer on Ray's forum too. Regards, Fred
The viewDidLoad
method is too early to set your view's frame because something else is changing the frame later.
For example, if this is your root view controller, then the UIWindow
object will set the view's frame when it adds the view to itself as a subview. The viewDidLoad
method runs as soon as loadView
returns, before any outside object (like the UIWindow
) can get its hands on the view.
You can test this by using a custom view (if you're not already) and overriding setFrame:
in the custom view class:
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
}
Put a breakpoint in that method and you'll see that the view's frame gets set some time after viewDidLoad
returns.
Rob Mayoff's answer is correct and excellent, but putting it a slightly different way: viewDidLoad
only means the view has loaded, i.e. that the view controller has obtained its view. It doesn't mean that the view has been placed in the interface. That, indeed, is one of the things that viewDidAppear:
does mean — and that's why it worked when you ran your code there.
The trick in this sort of situation, where you want to initialize something about the view, is to do it late enough but do it only once. viewDidAppear:
could easily be called again later, but you don't want to initialize the view again (unless it has been unloaded). In iOS 5, isMovingToParentViewController
allows you to distinguish the particular circumstances you're looking for. Before that, it might be necessary to set up a BOOL flag so that you perform final initializations only once.
A related trap is what happens when the app launches into landscape orientation. Here, too, viewDidLoad
is too soon because the interface has not yet rotated into landscape.
However, this issue should not be arising at all. It should be none of your business to inset a view controller's view. Either the view is the root view controller, in which case its size is correctly taken care of automatically, or it is the child of a parent view controller, in which case it is the parent view controller's job to size the view (as UINavigationController, for example, already does), or the view is to be presented modally, in which case its size will be set automatically to match the view it replaces. So I would suggest that you very question suggests you're doing something wrong.
Create your project with an older version of Xcode (for instance I'm using Xcode 4.3.3 for this) . Then you can use setFrame: method with viewDidLoad in any version of Xcode .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With