pasine
pasine

Reputation: 11543

Dynamic heightForHeaderInSection

How can I modify the height of a header section accordantly to the view I'm adding it?
heightForHeaderInSection is called before viewForHeaderInSection and I don't know the view size until I create it.

Upvotes: 5

Views: 8195

Answers (4)

Lilo
Lilo

Reputation: 2734

To force calling heightForHeaderInSection method, you just need to call both methods beginUpdates/endUpdates of the UITableView.

First create and init a variable height and return it

var height: CGFloat = 160.0

func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return height
}

Then add a method that will update the section height with a new value

func updateHeaderHeight(newHeight: CGFloat) {
    tableView.beginUpdates()
    height = newHeight
    tableView.endUpdates()
}

Upvotes: 1

Abhay Singh
Abhay Singh

Reputation: 586

You can follow this:

  1. Create a property for headerView (NSArray if multiple header views).
  2. Create the view in "heightForHeaderInSection" itself.
  3. Assign your view to property.
  4. return the height of your view in "heightForHeaderInSection".
  5. In "viewForHeaderInSection" return this property.

Upvotes: 12

Erum Huang
Erum Huang

Reputation: 307

This also works:

  1. Override estimatedHeightForHeaderInSection, return a CGFloat, whatever, but not 0.

  2. Create a CGFloat property to save the height for header view.

    var sectionHeaderViewHeight:CGFloat = 10.0 
    

    (The value is not important, but never set to 0.0, if you do this, viewForHeaderInSection will never be invoked, you will never have a section header view!)

  3. Override heightForHeaderInSection and return that property.

    override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return sectionHeaderViewHeight
    }
    
  4. Create the header view in viewForHeaderInSection

    override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
         let headerView = UIView()
         //configuration your view
         //.....
    
         return headerView
    }
    
  5. This is the key step: Add these code before return headerView.

    self.tableView.beginUpdates()
    sectionHeaderViewHeight = //The value you would like to set
    self.tableView.endUpdates()
    

It looks like this:

override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
   let headerView = UIView()
   //configuration your view
   //.....
   //----Key Steps:----
   self.tableView.beginUpdates()
   sectionHeaderViewHeight = //The value you would like to set
   self.tableView.endUpdates()
   //----Key Steps----
   return headerView
}

var sectionHeaderViewHeight:CGFloat = 10.0 
override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return sectionHeaderViewHeight
}

Upvotes: 3

Rob
Rob

Reputation: 437381

Yeah, it seems strange/redundant that you have to create both the view and then also determine the size of that view as a completely separate event, but you can minimize the silliness by moving some of that shared logic into some shared method. E.g., you probably have to figure out the size of the view at some point during it's creation, so move that logic to some shared method.

For example, I have logic that I use for determining the size of the UILabel I'm putting in my header based upon the size of the text. So I pulled that out of my viewForHeaderInSection and moved it into my own method, sizeForHeaderLabelInSection, which I use to determine the size of my label control):

- (CGSize)tableView:(UITableView *)tableView sizeForHeaderLabelInSection:(NSInteger)section
{
    NSString *text = [self tableView:tableView titleForHeaderInSection:section];

    CGSize constraint = CGSizeMake(self.view.frame.size.width - kSectionTitleLeftMargin - kSectionTitleRightMargin, kMaxSectionTitleHeight);
    return [text sizeWithFont:[self fontForSectionHeader] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
}

Then, I modified the standard heightForHeaderInSection to use that method, adding, of course, my top and bottom margin around my UILabel:

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{    
    return [self tableView:tableView sizeForHeaderLabelInSection:section].height + kSectionTitleTopMargin + kSectionTitleBottomMargin;
}

And then I modified the standard viewForHeaderInSection to also use this sizeForHeaderLabelInSection, too:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    CGSize size = [self tableView:tableView sizeForHeaderLabelInSection:section];

    UIView* headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, size.width + kSectionTitleLeftMargin + kSectionTitleRightMargin, size.height + kSectionTitleTopMargin + kSectionTitleBottomMargin)];
    //headerView.contentMode = UIViewContentModeScaleToFill;

    // Add the label
    UILabel *headerLabel = [[UILabel alloc] initWithFrame:CGRectMake(kSectionTitleLeftMargin, 
                                                                     kSectionTitleTopMargin, 
                                                                     size.width, 
                                                                     size.height)];
    // put stuff to set up my headerLabel here...

    [headerView addSubview:headerLabel];

    // Return the headerView
    return headerView;
}

Clearly, how you do this is completely up to you and what you're trying to achieve. But I think you'll have success if you shift your mindset from "how do I figure out the size of that view I created in viewForHeaderInSection" to "how can I move the the code that I used for determining the size in viewForHeaderInSection into some common method that my heightForSectionInHeader can use, too.

Upvotes: 3

Related Questions