7

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?

Community
  • 1
  • 1
Eric
  • 16,003
  • 15
  • 87
  • 139

2 Answers2

16

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...

WDUK
  • 18,870
  • 3
  • 64
  • 72
4

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.

calimarkus
  • 9,955
  • 2
  • 28
  • 48
  • 2
    I'd push it further down. Create your subview objects in `viewDidLoad` rather than init. views are loaded lazily, and there is no reason to create subviews if the superview isn't going to be created. – Abizern Nov 01 '12 at 13:13
  • 9
    We are talking about views here, not ViewController. – calimarkus Nov 01 '12 at 15:51