views:

44

answers:

2

I currently have a UIView subclass that acts as my header view for my UITableViewController. All of the subviews vary in size depending on data retrieved for a particular item.

layoutSubViews is getting called for the UIView before I can determine the size of each label. This causes a problem because I set the size of the view within the layoutSubViews method. Since it gets called before I setup my labels, the views height is always 0. Even after setting up the labels I call setNeedsLayout but the table views header size does not change.

This will create my TableHeaderView and set the text for my labels.

    TableHeaderView *tableHeaderView = [[TableHeaderView alloc] initWithFrame:CGRectZero];
    tableHeaderView.headerTitle.text = title;
    tableHeaderView.headerOption1.text = headerOption1
    tableHeaderView.headerOption2.text = headerOption2
    tableHeaderView.headerOption3.text = headerOption3

    [[self tableView] setTableHeaderView:tableHeaderView];

    [tableHeaderView setNeedsLayout];
    [tableHeaderView release];

Here is my UIView subclass

- (id)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {

     UIView *headerView = self;

     self.headerTitle = [[UILabel alloc] initWithFrame:CGRectZero];
     self.headerTitle.numberOfLines = 3;
     self.headerTitle.lineBreakMode = UILineBreakModeWordWrap;
     [headerView addSubview:self.headerTitle];
     [self.headerTitle release];

     self.headerOption1 = [[UILabel alloc] initWithFrame:CGRectZero];
     self.headerOption1.numberOfLines = 2;
     self.headerOption1.lineBreakMode = UILineBreakModeWordWrap;
     [headerView addSubview:self.headerOption1];
     [self.headerOption1 release];
 }
 return self;
}

- (void)layoutSubviews {

 [super layoutSubviews];

 CGSize maxLabelSize;

 /*...*/

 [self.headerTitle setFrame:CGRectMake(10.0f, 10.0f, titleWidth, titleHeight)];

 /*...*/

 [self.headerOption1 setFrame:CGRectMake(10.0f, (self.headerTitle.frame.origin.y + self.headerTitle.bounds.size.height + 2.5f), pubWidth, pubHeight)];

    /*...*/
    [self setFrame:CGRectMake(0.0f, 0.0f, 320.0f, tableHeaderHeight)];
}

The second time that layoutSubViews is called all of the subviews get sized correctly except for the view itself (tableHeaderHeight has the correct height). Should I not be resizing the view from this method? Is there a better approach to this?

+2  A: 

You likely need to override sizeThatFits: on your UIView subclass to return the appropriate size based on your layout.

Use it like this:

TableHeaderView *tableHeaderView = [[TableHeaderView alloc] initWithFrame:CGRectZero];
tableHeaderView.headerTitle.text = title;
tableHeaderView.headerOption1.text = headerOption1
tableHeaderView.headerOption2.text = headerOption2
tableHeaderView.headerOption3.text = headerOption3

tableHeaderView.frame = (CGRect){
    .origin = tableHeaderView.frame.origin,
    .size = [tableHeaderView sizeThatFits:CGSizeZero],
};

[[self tableView] setTableHeaderView:tableHeaderView];

[tableHeaderView setNeedsLayout]; // I don't think you need this anymore.
[tableHeaderView release];
Robot K
I tried your suggestion to call [tableHeaderView sizeToFit] after setting up the content of the labels, but I think my real problem here is that once setTableHeaderView:tableHeaderView is called, the size of the table header will not change. [tableHeaderView setNeedsLayout] does not invoke the layoutSubViews method until after the view has been set to the header. So no matter what size I then set to the view, its not effecting the size of the header.
avenged
I didn't say to call it, you need to **implement** sizeThatFits on your subclass and return the size that you want.
Robot K
Sorry, I first meant to say that I did implement sizeThatFits to return the proper size of the header view. The problem is that once sizeToFit has been called (I assume you want me to call it after setting up the labels), tableHeaderView has already been set to the table views header.
avenged
See my updated answer. I'm curious: Does it matter if you move the setNeedsLayout before adding it to the table view?
Robot K
No, I believe that setNeedsLayout only calls layoutSubviews when needed, so calling setNeedsLayout early makes no difference.I believe your code will work for what I am trying to do, but since layoutSubviews has not setup the size for my labels, I would have to duplicate all the code in layoutSubviews in the sizeThatFits method to determine the height of the view. Is calling layoutSubViews directly a bad thing?
avenged
I don't know about calling layoutSubViews directly. I've heard the same as you, but I can't find anything that says why.
Robot K
Well either approach seems to get the job done. For less complicated subviews, implementing sizeThatFits seems to work just fine. I just have a problem repeating so much code to retrieve the height of the view. Thanks for the help!
avenged
A: 

If I replace

[tableHeaderView setNeedsLayout];

with

[tableHeaderView layoutSubviews];

the header view is sized correctly. This is happening because setNeedsLayout does not call layoutSubViews until after the view has been set to the header view. If i directly call layoutSubViews however, layoutSubviews will be called before setting the view.

But I have heard in the past that directly calling layoutSubviews is not a good idea, is that also true for this case?

avenged