четверг, 6 ноября 2014 г.

Don't even think about setting frames at -viewDidLoad!


There is a common question: "Where should I initialize subviews (programmatically)?". Answer is quite complicated.

What do we need to create an object? Right: allocate memory and initialize object. What does this actually mean?
To initialize - to set starting values.

So, can you create subviews in one place? Oh, yes you can. But you should not.
The main factor is view's frame. It could be changed. And it should be set depending on current device orientation.

Q: Ok, where should I create subview and set initial parameters?
A: Let it be Rule #1: Create (allocate and init) subviews at -viewDidLoad. That method is called only once. Obviously, when your view controller was loaded from storyboard or xib.

Here is a simple example:
Firstly, we define our subviews at class extension (looks like a nameless category):
@interface ViewController ()
{
    UIView *subviewOne;
    UIImageView *imageView;
    UILabel *labelForBoldText;
}
@end

Secondly, we create subviews by allocating memory, defining attributes and adding them to main view:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    // Init subviews //
    // view
    subviewOne = [[UIView alloc] init];
    subviewOne.backgroundColor = [UIColor redColor];
    subviewOne.layer.cornerRadius = 20.0f;
    [self.view addSubview:subviewOne];
    
    // label
    labelForBoldText = [[UILabel alloc] init];
    labelForBoldText.backgroundColor = [UIColor clearColor];
    labelForBoldText.textColor = [UIColor blackColor];
    labelForBoldText.font = [UIFont boldSystemFontOfSize:20.0f];
    [self.view addSubview:labelForBoldText];
}

Last goal is to set the frame. And here comes the main question:

Q: What place is the best to set the frame?
A: In my opinion, -viewWillLayoutSubviews.
Why do I prefer this method to -viewWillAppear? Apple documentation says: "-viewWillAppear - Called when the view is about to made visible." So, it's used to handle changes in screens. But what if user just flipped device on the other side? -viewWillAppear will not be called. You don't need to think about it if you are using autoLayout, because it will make frame changes depending on constraints.
On the other hand, -viewWillLayoutSubviews will be called when orientation changes will take place and the new view frame is already set. So, you can update your subviews frame just before their -layoutSubview method will be called.

Frame setter code will look like:
- (void)viewWillLayoutSubviews {
    // Define frame for view
    CGRect viewFrame = CGRectMake(0, 0, 100, 100);
    subviewOne.frame = viewFrame;
    
    // Define frame for label
    CGRect labelFrame = CGRectMake(0, 100, 100, 20);
    labelForBoldText.frame = labelFrame;
}

To conclude, I suggest following steps for creating subviews programmatically:

  1. Define subviews at class extension to make it accessible 
  2. Initialize subviews and add to view at -viewDidLoad
  3. Set frames at -viewWillLayoutSubviews

Dismissed.
Contact me at twitter (@ibvene) if you have any questions.

Комментариев нет:

Отправить комментарий