How to use UITableViewHeaderFooterView?

IosObjective CUitableview

Ios Problem Overview


Hi I want to use UITableHeaderFooterView in my app and i am doing this:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [_tableView registerClass:[M3CTableViewCell class] forCellReuseIdentifier:@"cell"];
    [_tableView registerClass:[M3CHeaderFooter class] forHeaderFooterViewReuseIdentifier:@"footer"];
    
}

- (UITableViewHeaderFooterView *)footerViewForSection:(NSInteger)section {
    M3CHeaderFooter * footer = [[M3CHeaderFooter alloc]initWithReuseIdentifier:@"footer"];
    footer.textLabel.text = @"Test";
    return footer;
}

By doing this I am not getting anything at Footer's place. And this method is not even getting called but I think this method is part of UITableViewDelegate protocol.

Ios Solutions


Solution 1 - Ios

Using the new iOS 6 feature of reusable header/footer views involves two steps. You seem to be doing only the first step.

First step: you're telling the table view what class to use for the section header view, by registering your custom subclass of UITableViewHeaderFooterView (I assume your M3CHeaderFooter is a subclass of UITableViewHeaderFooterView).

Second step: Tell the table view what view to use (AND reuse) for a header section by implementing the tableView delegate method

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

So in your viewDidLoad you'd implement something like this:

    // ****** Do Step One ******
    [_tableView registerClass:[M3CHeaderFooter class] forHeaderFooterViewReuseIdentifier:@"TableViewSectionHeaderViewIdentifier"];

Then you'd implement the table View delegate method in the class where you're creating and displaying your table view:

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 40.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *headerReuseIdentifier = @"TableViewSectionHeaderViewIdentifier";
	
    // ****** Do Step Two *********
	M3CHeaderFooter *sectionHeaderView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
   // Display specific header title
   sectionHeaderView.textLabel.text = @"specific title";   
  
	return sectionHeaderView;    
}

Now mind you that you do not need to subclass UITableViewHeaderFooterView in order to use it. Before iOS 6, if you wanted to have a header view for a section, you'd implement the above tableView delegate method and tell the table view what view to use for each section. So each section had a different instance of a UIView which you provided. This means that if your tableView had 100 sections, and inside the delegate method you created a new instance of a UIView, then you would have given the tableView 100 UIViews for the 100 section headers that were displayed.

Using the new feature of reusable header/footer views, you create an instance of a UITableViewHeaderFooterView and the system reuses it for each displayed section header.

If you wanted to have a reusable UITableViewHeaderFooterView without subclassing then you simply change your viewDidLoad to this:

// Register the class for a header view reuse.
[_buttomTableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:@"TableViewSectionHeaderViewIdentifier"];

and then your delegate method to this:

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 40.0;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *headerReuseIdentifier = @"TableViewSectionHeaderViewIdentifier";
	
   // Reuse the instance that was created in viewDidLoad, or make a new one if not enough.
	UITableViewHeaderFooterView *sectionHeaderView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:headerReuseIdentifier];
	sectionHeaderView.textLabel.text = @"Non subclassed header";
    
	return sectionHeaderView;

}

I hope that was clear enough.

EDIT: When subclassing the header view, you can implement code similar to the following if you wish to add a custom view to the headerView:

        // Add any optional custom views of your own
    UIView *customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 30.0)];
    [customView setBackgroundColor:[UIColor blueColor]];

    [sectionHeaderView.contentView addSubview:customView];

Doing this in the subclass, as opposed to viewForHeaderInSection: delegate method (as noted below by Matthias), will ensure that only one instance of any subviews are created. You can then add any properties within the subclass that will allow you to access your custom subview.

Solution 2 - Ios

UITableViewHeaderFooterView is one of the few places I would programmatically handle the view rather than use Storyboard or a XIB. Since you cannot officially use appearance proxy and there is no IB way to do it without abusing UITableViewCells. I do it the old-fashioned way and just use the tag on the label to fetch the custom elements.

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kSectionHeaderReuseIdentifier];
    if (headerView == nil) {
        [tableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:kSectionHeaderReuseIdentifier];
        headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kSectionHeaderReuseIdentifier];
    }
    
    UILabel *titleLabel = (UILabel *)[headerView.contentView viewWithTag:1];
    if (titleLabel == nil) {
        UIColor *backgroundColor = [UIColor blackColor];
        headerView.contentView.backgroundColor = backgroundColor;
        titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 0.0f, 300.0f, 44.0f)];
        titleLabel.textColor = [UIColor whiteColor];
        titleLabel.backgroundColor = backgroundColor;
        titleLabel.shadowOffset = CGSizeMake(0.0f, 0.0f);
        titleLabel.tag = 1;
        titleLabel.font = [UIFont systemFontOfSize:24.0f];
        [headerView.contentView addSubview:titleLabel];
    }
    
    NSString *sectionTitle = [self.sections objectAtIndex:section];
    if (sectionTitle == nil) {
        sectionTitle = @"Missing Title";
    }

    titleLabel.text = sectionTitle;
    
    return headerView;
}

Solution 3 - Ios

This is an old post and has good answers, but I wanted to share another work-around for a very similar issue I experienced.

At first, I used:

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

With a custom prototype cell for my header view. Subclassing UITableViewCell as such

    static NSString *cellIdentifier = @"CustomHeaderCell";
CustomHeaderCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

However, when animating TableView cells above section headers (making them twice as tall) the header view would disappear. This, as pointed out, is because the implementation only supplied a view, not a re-usable view.

Instead of forgoing everything with the customized prototype cell, I implemented the UITableViewHeaderFooterWithIdentifier and set it as the prototyped cell's contentView, without subclassing UITableViewHeaderFooterWithIdentifier.

  static NSString *customHeaderViewIdentifier = @"CustomHeaderView";
UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:customHeaderViewIdentifier];

headerView = (UITableViewHeaderFooterView *)cell.contentView;

I realize this creates two instances of the header view (at least I think it would..) however it does allow you to keep the benefits of a customized prototype cell without doing everything programatically.

Full code:

  // viewDidLoad
    [myTableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:@"CustomHeaderView"];

// Implement your custom header
 -(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
     static NSString *cellIdentifier = @"CustomHeaderCell";
    CustomHeaderCell * cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    static NSString *customHeaderViewIdentifier = @"CustomHeaderView";
    UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:customHeaderViewIdentifier];

// do your cell-specific code here
// eg. cell.myCustomLabel.text = @"my custom text"

    headerView = (UITableViewHeaderFooterView *)cell.contentView;

return headerView;
}

Solution 4 - Ios

There are a few ways of approaching this, but here is one a solution in Swift: the idea here is that we have a subclass of UITableViewHeaderFooterView called SNStockPickerTableHeaderView; it exposes a method called, configureTextLabel() that when called, sets the font and the color of the text label. We call this method only after the title has been set, that is from, willDisplayHeaderView, and the font gets correctly set.

The header view also supports a custom line separator to set it apart from the rest of the cells.

// MARK: UITableViewDelegate

func tableView(tableView:UITableView, willDisplayHeaderView view:UIView, forSection section:Int) {
  if let headerView:SNStockPickerTableHeaderView = view as? SNStockPickerTableHeaderView {
    headerView.configureTextLabel()
  }
}

func tableView(tableView:UITableView, viewForHeaderInSection section:Int) -> UIView? {
  var headerView:SNStockPickerTableHeaderView? = tableView.dequeueReusableHeaderFooterViewWithIdentifier(kSNStockPickerTableHeaderViewReuseIdentifier) as? SNStockPickerTableHeaderView
  if (headerView == nil) {
    // Here we get to customize the section, pass in background color, text 
    // color, line separator color, etc. 
    headerView = SNStockPickerTableHeaderView(backgroundColor:backgroundColor,
      textColor:primaryTextColor,
      lineSeparatorColor:primaryTextColor)
  }
  return headerView!
}

And here is the custom UITableViewHeaderFooterView:

import Foundation
import UIKit

private let kSNStockPickerTableHeaderViewLineSeparatorHeight:CGFloat = 0.5
private let kSNStockPickerTableHeaderViewTitleFont = UIFont(name:"HelveticaNeue-Light", size:12)

let kSNStockPickerTableHeaderViewReuseIdentifier:String = "stock_picker_table_view_header_reuse_identifier"

class SNStockPickerTableHeaderView: UITableViewHeaderFooterView {
 
  private var lineSeparatorView:UIView?
  private var textColor:UIColor?
  
  required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
  
  // We must implement this, since the designated init of the parent class
  // calls this by default!
  override init(frame:CGRect) {
    super.init(frame:frame)
  }
  
  init(backgroundColor:UIColor, textColor:UIColor, lineSeparatorColor:UIColor) {
    super.init(reuseIdentifier:kSNStockPickerTableHeaderViewReuseIdentifier)
    contentView.backgroundColor = backgroundColor
    self.textColor = textColor
    addLineSeparator(textColor)
  }
  
  // MARK: Layout
  
  override func layoutSubviews() {
    super.layoutSubviews()
    let lineSeparatorViewY = CGRectGetHeight(self.bounds) - kSNStockPickerTableHeaderViewLineSeparatorHeight
    lineSeparatorView!.frame = CGRectMake(0,
      lineSeparatorViewY,
      CGRectGetWidth(self.bounds),
      kSNStockPickerTableHeaderViewLineSeparatorHeight)
  }
  
  // MARK: Public API
  
  func configureTextLabel() {
    textLabel.textColor = textColor
    textLabel.font = kSNStockPickerTableHeaderViewTitleFont
  }
  
  // MARK: Private
  
  func addLineSeparator(lineSeparatorColor:UIColor) {
    lineSeparatorView = UIView(frame:CGRectZero)
    lineSeparatorView!.backgroundColor = lineSeparatorColor
    contentView.addSubview(lineSeparatorView!)
  }
}

Here is the result, see section header for, "Popular Stocks":

                              enter image description here

Solution 5 - Ios

I can't comment under Cameron Lowell Palmer post but to answer Christopher King, there is a simple way to ensure the re-use without sub-classing UITableViewHeaderFooterView and yet still using custom subviews.

First, do NOT register the class for a header view reuse.

Then in tableView:viewForHeaderInSection: you simply have to create UITableViewHeaderFooterView when needed:

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *kYourTableViewReusableHeaderIdentifier = @"ID";

    UILabel *titleLabel = nil;

    UITableViewHeaderFooterView *headerView = [tableView dequeueReusableHeaderFooterViewWithIdentifier:kYourTableViewReusableHeaderIdentifier];

    if (headerView == nil) {
    
        headerView = [[UITableViewHeaderFooterView alloc] initWithReuseIdentifier:kYourTableViewReusableHeaderIdentifier];
    
        titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(...)];
        titleLabel.tag = 1;
        // ... setup titleLabel 

        [headerView.contentView addSubview:titleLabel];
    } else {
        // headerView is REUSED
        titleLabel = (UILabel *)[headerView.contentView viewWithTag:1];
    }

    NSString *sectionTitle = (...); // Fetch value for current section
    if (sectionTitle == nil) {
        sectionTitle = @"Missing Title";
    }

    titleLabel.text = sectionTitle;

    return headerView;
}

Solution 6 - Ios

Here is a "quick-and-dirty" way to get this going. It will make a small blue label in the header. I've confirmed that this renders OK in iOS 6 and iOS 7.

in your UITableViewDelegate:

 -(void)viewDidLoad
{
...
    [self.table registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:@"Header"];
...
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 34.;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    UITableViewHeaderFooterView *header = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"Header"];
    
    UILabel *leftlabel = [[UILabel alloc] initWithFrame:CGRectMake(0., 0., 400., 34.)];
    [leftlabel setBackgroundColor:[UIColor blueColor]];
    
    [header.contentView addSubview:leftlabel];
    return header;
}

Solution 7 - Ios

In case it gets lost in the thorough answers above, the thing that people are likely missing (compared to the standard cellForRowAtIndexPath: method) is that you must register the class used for the section header.

[tableView registerClass:[UITableViewHeaderFooterView class] forHeaderFooterViewReuseIdentifier:@"SectionHeader"];

Try adding registerClass:forHeaderFooterViewReuseIdentifier: and see if it starts working.

Solution 8 - Ios

One of the reasons that method may not be being called is the style of the table. Standard vs Grouped handles headers/footers differently. That may explain why it's not getting called.

Solution 9 - Ios

  1. Set delegate property of UITableView instance to reference to the controller that implements next methods:

  2. Method that returns view of section footer:

> Asks the delegate for a view object to display in the footer of the specified section of the table view. >- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section

  1. Height of view in section footer:

    >Asks the delegate for the height to use for the footer of a particular section.

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAshutoshView Question on Stackoverflow
Solution 1 - IosRazView Answer on Stackoverflow
Solution 2 - IosCameron Lowell PalmerView Answer on Stackoverflow
Solution 3 - IosEricKView Answer on Stackoverflow
Solution 4 - IosZorayrView Answer on Stackoverflow
Solution 5 - IosBluezenView Answer on Stackoverflow
Solution 6 - IosmazView Answer on Stackoverflow
Solution 7 - IospkambView Answer on Stackoverflow
Solution 8 - IosMark A. DonohoeView Answer on Stackoverflow
Solution 9 - IosNektoView Answer on Stackoverflow