-
Notifications
You must be signed in to change notification settings - Fork 1
DD 4.5. Constructing Headers and Footers in Table Views
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; }