Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIView: layoutSubviews vs initWithFrame

When subclassing UIView, I usually place all my initialisation and layout code in its init method. But I'm told that the layout code should be done by overriding layoutSuviews. There's a post on SO that explains when each method gets called, but I'd like to know how to use them in practice.

I currently put all my code in the init method, like this:

MyLongView.m

- (id)initWithHorizontalPlates:(int)theNumberOfPlates
{
    self = [super initWithFrame:CGRectMake(0, 0, 768, 1024)];

    if (self) {
        // Initialization code
        _numberOfPlates = theNumberOfPlates;

        UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.frame];
        [scrollView setContentSize:CGSizeMake(self.bounds.size.width* _numberOfPlates, self.bounds.size.height)];
        [self addSubview:scrollView];

        for(int i = 0; i < _numberOfPlates; i++){
            UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:@"a1greatnorth_normal_%d.jpg", i+1]];
            UIImageView *plateImage = [[UIImageView alloc] initWithImage:img];
            [scrollView addSubview:plateImage];
            plateImage.center = CGPointMake((plateImage.bounds.size.width/2) + plateImage.bounds.size.width*i, plateImage.bounds.size.height/2);
        }

    }
    return self;
}

It's the usual tasks: setting up the view's frame, initialising an ivar, setting up a scrollview, initialising UIImages, placing them in UIImageViews, laying them out.

My question is: which of these should be done in init, and which of these should be done in layoutSubviews?

like image 931
Eric Avatar asked Nov 01 '12 12:11

Eric


People also ask

When should I use layoutSubviews?

layoutSubviews() The system calls this method whenever it needs to recalculate the frames of views, so you should override it when you want to set frames and specify positioning and sizing. However, you should never call this explicitly when your view hierarchy requires a layout refresh.

How many times is layoutSubviews called?

layoutSubviews is called when the view is created, but never again. My subviews are correctly laid out. If I toggle the in-call status bar off, the subview's layoutSubviews is not called at all, even though the main view does animate its resize.

When can I call super layoutSubviews?

For more recent versions of iOS, you should call [super layoutSubviews] at the beginning of your implementation. Otherwise, the superclass will rearrange your subviews after you do the custom layout, effectively ignoring your implementation of layoutSubviews() .


2 Answers

Your init should create all the objects, with the required data. Any frame you pass to them in init should ideally be their starting positions.

Then, within layoutSubviews:, you change the frames of all your elements to place them where they should go. No alloc'ing or init'ing should take place in layoutSubviews:, only the changing of their positions, sizes etc...

like image 109
WDUK Avatar answered Oct 15 '22 09:10

WDUK


In case you're autoresizing works perfectly with just autoresizingFlags, or autolayout, you may just use init to setup the whole view.

But in general your should do layouting in layoutSubviews, since this will be called on every change of the views frame and in other situation, where layout is needed again. Sometimes you just don't know the final frame of a view within init, so you need to be flexible as mentioned, or use layoutSubviews, since you do the layout there after the final size has been set.

As mentioned by WDUK, all initialization code / object creation should be in your init method or anywhere, but not in layoutSubviews.

like image 43
calimarkus Avatar answered Oct 15 '22 08:10

calimarkus