Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cannot update view.layer.frame in viewDidLoad?

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.

like image 387
dotnetcoder Avatar asked Feb 28 '12 04:02

dotnetcoder


4 Answers

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 :

  • in viewDidLoad, the properties are set, but the frame of the views are not yet defined.
  • by the time viewWillAppear is triggered, they will be set. That explains why that line of code works there.
  • But since iOS 5, there is another method called after viewDidLoad and before viewWillAppear in which the view frames are set : viewDidLayoutSubviews.

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

like image 172
Frédéric Adda Avatar answered Nov 10 '22 18:11

Frédéric Adda


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.

like image 25
rob mayoff Avatar answered Nov 10 '22 18:11

rob mayoff


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.

like image 5
matt Avatar answered Nov 10 '22 18:11

matt


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 .

like image 2
Salih Ozdemir Avatar answered Nov 10 '22 17:11

Salih Ozdemir