Skip to content

DD 4.5. Constructing Headers and Footers in Table Views

rayastar edited this page Feb 8, 2015 · 1 revision

Problem

You want to create a header and/or a footer for a table view.

Solution

Create a view (could be a label, image view, etc., anything that directly or indirectly subclasses UIView), and assign that view to the header and/or the footer of a section of a table view. You can also allocate a specific number of points in height for a header or a footer, as we will soon see.

A table view can have multiple headers and footers. Each section in a table view can have its own header and footer, so if you have three sections in a table view, you can have a maximum of three headers and a maximum of three footers. You are not obliged to provide headers and footers for any of these sections. It is up to you to tell the table view whether you want a header and/or a footer for a section, and you pass these views to the table view through its delegate, should you wish to provide header(s)/footer(s) for section(s) of your table view. Headers and footers in a table view become a part of the table view, meaning that when the table view’s contents scroll, so do the header(s) and footer(s) inside that table view.

Let’s go ahead and create a simple app with one table view in it. Then let’s provide two labels, of type UILabel, one as the header and the other as the footer of the only section in our table view, and populate this one section with only three cells. In the header we will place the text “Section 1 Header,” and in the footer label we will place the text “Section 1 Footer.” Starting with the implementation file of our root view controller, we will define a table view:

#import "ViewController.h"

static NSString *CellIdentifier = @"CellIdentifier";

@interface ViewController () <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) UITableView *myTableView;
@end

Now we will create a grouped table view and load three cells into it:

- (UITableViewCell *) tableView:(UITableView *)tableView
          cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    UITableViewCell *cell = nil;

    cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
                                           forIndexPath:indexPath];

    cell.textLabel.text = [[NSString alloc] initWithFormat:@"Cell %ld",
                             (long)indexPath.row];

    return cell;

}

- (NSInteger) tableView:(UITableView *)tableView
  numberOfRowsInSection:(NSInteger)section{
    return 3;
}

- (void)viewDidLoad{
    [super viewDidLoad];

    self.myTableView =
    [[UITableView alloc] initWithFrame:self.view.bounds
                                 style:UITableViewStyleGrouped];

    [self.myTableView registerClass:[UITableViewCell class]
             forCellReuseIdentifier:CellIdentifier];

    self.myTableView.dataSource = self;
    self.myTableView.delegate = self;
    self.myTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
    UIViewAutoresizingFlexibleHeight;

    [self.view addSubview:self.myTableView];

}

Here is the exciting part. We can now use two important methods (which are defined in UITableViewDelegate) to provide a label for the header and another label for the footer of the one section that we have loaded into our table view: tableView:viewForHeaderInSection: This method expects a return value of type UIView. The view returned from this method will be displayed as the header of the section specified by the viewForHea derInSection parameter. tableView:viewForFooterInSection: This method expects a return value of type UIView. The view returned from this method will be displayed as the footer of the section specified by the viewForFoo terInSection parameter.

Our task now is to implement these methods and return an instance of UILabel. On the header label we will enter the text “Section 1 Header,” and on the footer label the text “Section 1 Footer,” as we had planned:

- (UILabel *) newLabelWithTitle:(NSString *)paramTitle{
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
    label.text = paramTitle;
    label.backgroundColor = [UIColor clearColor];
    [label sizeToFit];
    return label;
}

- (NSString *) tableView:(UITableView *)tableView
 titleForHeaderInSection:(NSInteger)section{

    if (section == 0){
        return @"Section 1 Header";
    }

    return nil;

}

- (NSString *) tableView:(UITableView *)tableView
 titleForFooterInSection:(NSInteger)section{

    if (section == 0){
        return @"Section 1 Footer";
    }

    return nil;

}

If you run app you look that the header and footer labels of a table view are not aligned properly. The reason for this misalignment of the labels and the omission of the header is that the table view doesn’t really know the height of these views. To specify the height of the header and footer views, you need to use the following two methods, which are defined in the UITableViewDelegate protocol:

tableView:heightForHeaderInSection: The return value of this method, of type CGFloat, specifies the height of the header for a section in a table view. The section’s index is passed through the heightFo rHeaderInSection parameter.

tableView:heightForFooterInSection: The return value of this method, of type CGFloat, specifies the height of the footer for a section in a table view. The section’s index is passed through the heightFo rHeaderInSection parameter.

- (CGFloat)     tableView:(UITableView *)tableView
 heightForHeaderInSection:(NSInteger)section{

    if (section == 0){
        return 50.0f;
    }

    return 0.0f;
}

- (CGFloat)     tableView:(UITableView *)tableView
 heightForFooterInSection:(NSInteger)section{

    if (section == 0){
        return 30.0f;
    }

    return 0.0f;

}

Running the app, you can see that the height of the header and the footer labels is fixed. There is still something wrong with the code we’ve written—the left margin of our header and footer labels.

The reason for this is that the table view, by default, places header and footer views at x point 0.0f. You might think that changing the frame of your header and footer labels will fix this issue, but unfortunately it doesn’t. The solution to this problem is creating a generic UIView and placing your header and footer labels on that view. Return the generic view as the header/footer, but change the x position of your labels within the generic view. We now need to modify our implementation of the tableView:viewFor HeaderInSection: and the tableView:viewForFooterInSection: methods:

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

    UIView *header = nil;

    if (section == 0){

        UILabel *label = [self newLabelWithTitle:@"Section 1 Header"];

        /* Move the label 10 points to the right */
        label.frame = CGRectMake(label.frame.origin.x + 15.0f,
                                 5.0f, /* Go 5 points down in y axis */
                                 label.frame.size.width,
                                 label.frame.size.height);

        /* Give the container view 10 points more in width than our label
         because the label needs a 10 extra points left-margin */
        CGRect resultFrame = CGRectMake(0.0f,
                                        0.0f,
                                        label.frame.size.width + 15.0f,
                                        label.frame.size.height);
        header = [[UIView alloc] initWithFrame:resultFrame];
        [header addSubview:label];

    }

    return header;

}

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

    UIView *footer = nil;

    if (section == 0){

        UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];

        /* Move the label 10 points to the right */
        label.frame = CGRectMake(label.frame.origin.x + 15.0f,
                                 5.0f, /* Go 5 points down in y axis */
                                 label.frame.size.width,
                                 label.frame.size.height);

        /* Give the container view 10 points more in width than our label
         because the label needs a 10 extra points left-margin */
        CGRect resultFrame = CGRectMake(0.0f,
                                        0.0f,
                                        label.frame.size.width + 15.0f,
                                        label.frame.size.height);
        footer = [[UIView alloc] initWithFrame:resultFrame];
        [footer addSubview:label];

    }

    return footer;

}
Clone this wiki locally