Design UITableView's section header in Interface Builder

#Storyboard or XIB. Updated for 2020.

  1. Same Storyboard:

     return tableView.dequeueReusableCell(withIdentifier: "header")
    

    Two Section Headers

  2. Separate XIB (Additional step: you must register that Nib first):

     tableView.register(UINib(nibName: "XIBSectionHeader", bundle:nil),
                        forCellReuseIdentifier: "xibheader")
    

To load from a Storyboard instead of a XIB, see this Stack Overflow answer.


#Using UITableViewCell to create Section Header in IB

Take advantage of the fact that a section header is a regular UIView, and that UITableViewCell is, too, a UIView. In Interface Builder, drag & drop a Table View Cell from the Object Library onto your Table View Prototype Content.

(2020) In modern Xcode, simply increase the "Dynamic Prototypes" number to drop in more cells:

enter image description here

Add an Identifier to the newly added Table View Cell, and customize its appearance to suit your needs. For this example, I used header.

Edit the cell

Use dequeueReusableCell:withIdentifier to locate the cell, just like you would any table view cell.

Don't forget it is just a normal cell: but you are going to use it as a header.

For 2020, simply add to ViewDidLoad the four lines of code:

tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 70   // any reasonable value is fine
tableView.sectionHeaderHeight =  UITableView.automaticDimension
tableView.estimatedSectionHeaderHeight = 70 // any reasonable value is fine

{See for example this for a discussion.}

Your header cell heights are now completely dynamic. It's fine to change the length of the texts, etc, in the headers.

(TiP: Purely regarding the storyboard: simply select...

enter image description here

...in storyboard, so that the storyboard will work correctly. This has absolutely no effect on the final build. Selecting that checkbox has absolutely no effect whatsoever on the final build. It purely exists to make the storyboard work correctly, if the height is dynamic.)


In older Xcode, or, if for some reason you do not wish to use dynamic heights:

simply supply heightForHeaderInSection, which is hardcoded as 44 for clarity in this example:

//MARK: UITableViewDelegate
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView?
{
    // This is where you would change section header content
    return tableView.dequeueReusableCell(withIdentifier: "header")
}

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

###Swift 2 & earlier:

return tableView.dequeueReusableCellWithIdentifier("header") as? UIView
self.tableView.registerNib(UINib(nibName: "XIBSectionHeader", bundle:nil),
    forCellReuseIdentifier: "xibheader")

► Find this solution on GitHub and additional details on Swift Recipes.


I finally solved it using this tutorial, which, largely consists of the following (adapted to my example):

  1. Create SectionHeaderView class that subclasses UIView.
  2. Create SectionHeaderView.xib file and set it's File's Owner's CustomClass to the SectionHeaderView class.
  3. Create an UIView property in the .m file like: @property (strong, nonatomic) IBOutlet UIView *viewContent;
  4. Connect the .xib's View to this viewContent outlet.
  5. Add an initializer method that looks like this:

    + (instancetype)header {
    
        SectionHeaderView *sectionHeaderView = [[SectionHeaderView alloc] init];
    
        if (sectionHeaderView) { // important part
            sectionHeaderView.viewContent = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:sectionHeaderView options:nil] firstObject];
    
            [sectionHeaderView addSubview:sectionHeaderView.viewContent];
    
            return sectionHeaderView;
        }
    
        return nil;
    }
    

Then, I added an UILabel inside the .xib file and connected it to the labelCategoryName outlet and implemented the setCategoryName: method inside the SectionHeaderView class like this:

- (void)setCategoryName:(NSString *)categoryName {

    self.labelCategoryName.text = categoryName;
}

I then implemented the tableView:viewForHeaderInSection: method like this:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

    SectionHeaderView *sectionHeaderView = [SectionHeaderView header];

    [sectionHeaderView setCategoryName:self.categoriesNames[section]];

    return sectionHeaderView;
}

And it finally worked. Every section has it's own name, and also UIImageViews show up properly.

Hope it helps others that stumble over the same wrong solutions over and over again, all over the web, like I did.


Solution Is way simple

Create one xib, make UI according to your Documentation then in viewForHeaderInSection get xib

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {

        NSArray *nibArray = [[NSBundle mainBundle] loadNibNamed:@"HeaderView" owner:self options:nil];

       HeaderView *headerView = [nibArray objectAtIndex:0];

    return headerView;
}