web-dev-qa-db-fra.com

Comment utiliser UITableViewHeaderFooterView?

Bonjour, je souhaite utiliser UITableHeaderFooterView dans mon application et je le fais:

- (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;
}

En faisant cela, je ne reçois rien chez Footer . Et cette méthode n'est même pas appelée mais je pense que cette méthode fait partie du protocole UITableViewDelegate.

45
Ashutosh

L'utilisation de la nouvelle fonctionnalité iOS 6 de vues d'en-tête/pied de page réutilisables implique deux étapes. Vous semblez ne faire que la première étape.

Première étape: vous indiquez à la vue table quelle classe utiliser pour la vue en-tête de section en enregistrant votre sous-classe personnalisée de UITableViewHeaderFooterView (je suppose que votre M3CHeaderFooter est une sous-classe de UITableViewHeaderFooterView). 

Deuxième étape: Indiquez à la vue table quelle vue utiliser (et à réutiliser) pour une section d’en-tête en implémentant la méthode déléguée tableView

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

Donc, dans votre viewDidLoad, vous implémenteriez quelque chose comme ceci:

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

Ensuite, vous implémenterez la méthode de délégué table View dans la classe où vous créez et affichez votre vue table:

- (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;    
}

Notez maintenant que vous n'avez pas besoin de sous-classe UITableViewHeaderFooterView pour pouvoir l'utiliser . Avant iOS 6, si vous souhaitiez avoir une vue en-tête pour une section, implémentiez la méthode déléguée tableView ci-dessus et indiquez la vue tableau. quelle vue utiliser pour chaque section. Ainsi, chaque section avait une instance différente d’UIView que vous aviez fournie. Cela signifie que si votre tableView avait 100 sections et que, dans la méthode de délégation, vous créiez une nouvelle instance d'un UIView, vous auriez donné la tableView 100 UIViews aux 100 en-têtes de section affichés. 

À l'aide de la nouvelle fonctionnalité des vues d'en-tête/pied de page réutilisables, vous créez une instance d'un UITableViewHeaderFooterView et le système la réutilise pour chaque en-tête de section affiché.

Si vous voulez avoir un UITableViewHeaderFooterView réutilisable sans sous-classement, vous devez simplement changer votre viewDidLoad en ceci:

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

et ensuite votre méthode déléguée à ceci:

- (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;

}

J'espère que c'était assez clair.

EDIT: Lorsque vous sous-classez la vue d'en-tête, vous pouvez implémenter un code similaire au suivant si vous souhaitez ajouter une vue personnalisée à 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];

Faire cela dans la sous-classe, par opposition à la méthode viewForHeaderInSection: delegate (comme noté par Matthias), garantira la création d'une seule instance de toutes les sous-vues. Vous pouvez ensuite ajouter des propriétés dans la sous-classe qui vous permettront d'accéder à votre sous-vue personnalisée.

68
Raz

UITableViewHeaderFooterView est l’un des rares endroits où je gérerais la vue par programme plutôt que d’utiliser Storyboard ou un XIB. Étant donné que vous ne pouvez pas utiliser officiellement le proxy de présentation et qu'il n'y a pas de moyen IB de le faire sans abuser de UITableViewCells. Je le fais à l'ancienne et j'utilise simplement l'étiquette sur l'étiquette pour récupérer les éléments personnalisés. 

- (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;
}
9

Ceci est un ancien post et a de bonnes réponses, mais je voulais partager une autre solution pour un problème très similaire que j'ai vécu.

Au début, j'ai utilisé:

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

Avec une cellule prototype personnalisée pour ma vue en-tête. Sous classer UITableViewCell en tant que tel

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

Toutefois, lors de l'animation de cellules TableView au-dessus des en-têtes de section (en les rendant deux fois plus hautes), la vue en-tête disparaîtrait. Comme indiqué, ceci est dû au fait que la mise en œuvre ne fournissait qu'une vue, et non une vue réutilisable. 

Au lieu de tout renvoyer avec la cellule prototype personnalisée, j'ai implémenté UITableViewHeaderFooterWithIdentifier et je l'ai défini comme contentView de la cellule prototypée, sans sous-classe UITableViewHeaderFooterWithIdentifier.

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

headerView = (UITableViewHeaderFooterView *)cell.contentView;

Je me rends compte que cela crée deux instances de la vue d’en-tête (du moins, je pense que ce serait le cas ..), mais cela vous permet de conserver les avantages d’une cellule prototype personnalisée sans tout effectuer par programme.

Code complet:

  // 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;
}
7
EricK

Il existe plusieurs manières d’aborder ceci, mais voici une solution dans Swift : l’idée est de créer une sous-classe de UITableViewHeaderFooterView appelée SNStockPickerTableHeaderView; il expose une méthode appelée configureTextLabel() qui, lorsqu'elle est appelée, définit la police et la couleur de l'étiquette de texte. Nous appelons cette méthode uniquement après que le titre a été défini, c'est-à-dire from, willDisplayHeaderView, et que la police soit correctement définie. 

La vue en-tête prend également en charge un séparateur de ligne personnalisé pour le distinguer du reste des cellules.

// 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!
}

Et voici la coutume 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!)
  }
}

Voici le résultat, voir en-tête de section pour "Actions populaires":

enter image description here

5
Zorayr

Je ne peux pas commenter sous Cameron Lowell Palmer post mais pour répondre à Christopher King, il existe un moyen simple d’assurer la réutilisation sans sous-classe UITableViewHeaderFooterView tout en utilisant des sous-vues personnalisées.

Tout d'abord, n'enregistrez PAS la classe pour une réutilisation de la vue d'en-tête.

Ensuite, dans tableView: viewForHeaderInSection: vous devez simplement créer UITableViewHeaderFooterView si nécessaire:

-(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;
}
3
Bluezen

Voici un moyen "rapide et sale" de faire avancer les choses. Cela créera une petite étiquette bleue dans l'en-tête. J'ai confirmé que cela rend bien sous iOS 6 et iOS 7.

dans votre 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;
}
1
maz

Au cas où il se perdrait dans les réponses détaillées ci-dessus, ce qui manque probablement aux gens (comparé à la méthode cellForRowAtIndexPath: standard) est que vous devez enregistrer la classe utilisée pour l'en-tête de section.

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

Essayez d’ajouter registerClass:forHeaderFooterViewReuseIdentifier: et voyez s’il commence à fonctionner.

0
pkamb

L'une des raisons pour lesquelles cette méthode peut ne pas être appelée est le style de la table. Standard vs Grouped gère les en-têtes/pieds de page différemment. Cela peut expliquer pourquoi on ne l'appelle pas.

0
MarqueIV