Comment implémenter les en-têtes de section et les pieds de page de tableau personnalisé avec Storyboard

Sans utiliser un storyboard, nous pourrions simplement faire glisser un UIView sur le canevas, le mettre en forme puis le définir dans les méthodes de délégué tableView:viewForHeaderInSection ou tableView:viewForFooterInSection .

Comment pouvons-nous accomplir cela avec un StoryBoard où nous ne pouvons pas faire glisser un UIView sur le canevas

    Utilisez simplement une cellule prototype comme en-tête de section et / ou en pied de page.

    • Ajouter une cellule supplémentaire et y mettre vos éléments souhaités.
    • définir l’identifiant à quelque chose de spécifique (dans mon cas SectionHeader)
    • implémenter la tableView:viewForHeaderInSection: ou la tableView:viewForFooterInSection: méthode
    • utilisez [tableView dequeueReusableCellWithIdentifier:] pour obtenir l’en-tête
    • implémenter la tableView:heightForHeaderInSection:

    (voir la capture d'écran)

     -(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSSsortingng *CellIdentifier = @"SectionHeader"; UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (headerView == nil){ [NSException raise:@"headerView == nil.." format:@"No cells with matching CellIdentifier loaded from your storyboard"]; } return headerView; } 

    Edit: Comment changer le titre de l’en-tête (question commentée):

    1. Ajouter une étiquette à la cellule d’en-tête
    2. définir l’étiquette de l’étiquette sur un nombre spécifique (par exemple, 123)
    3. Dans votre tableView:viewForHeaderInSection: méthode obtenez l’étiquette en appelant:
      UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
    1. Maintenant, vous pouvez utiliser l’étiquette pour définir un nouveau titre:
      [label setText:@"New Title"]; 

    Je sais que cette question concernait iOS 5, mais pour le bénéfice des futurs lecteurs, notez qu’à partir d’iOS 6, nous pouvons maintenant utiliser dequeueReusableHeaderFooterViewWithIdentifier au lieu de dequeueReusableCellWithIdentifier .

    Donc, dans viewDidLoad , appelez soit registerNib:forHeaderFooterViewReuseIdentifier: ou registerClass:forHeaderFooterViewReuseIdentifier: Ensuite, dans viewForHeaderInSection , appelez tableView:dequeueReusableHeaderFooterViewWithIdentifier: Vous n’utilisez pas un prototype de cellule avec cette API (c’est une vue basée sur NIB ou une vue créée par programmation), mais il s’agit de la nouvelle API pour les en-têtes et les pieds de page mis en queue.

    Dans iOS 6.0 et les dequeueReusableHeaderFooterViewWithIdentifier , les choses ont changé avec la nouvelle API dequeueReusableHeaderFooterViewWithIdentifier .

    J’ai écrit un guide (testé sur iOS 9), qui peut être résumé comme suit:

    1. Sous-classe UITableViewHeaderFooterView
    2. Créez Nib avec la vue de sous-classe et ajoutez 1 vue de conteneur qui contient toutes les autres vues dans l’en-tête / le pied de page.
    3. Enregistrez le Nib dans viewDidLoad
    4. Implémentez viewForHeaderInSection et utilisez dequeueReusableHeaderFooterViewWithIdentifier pour récupérer l’en-tête / le pied de page

    Je l’ai fait fonctionner dans iOS7 en utilisant une cellule prototype dans le storyboard. J’ai un bouton dans la vue d’en-tête de la section personnalisée qui déclenche une segmentation définie dans le storyboard.

    Commencez avec la solution de Tieme

    Comme le souligne pedro.m, le problème est que le fait de toucher l’en-tête de la section entraîne la sélection de la première cellule de la section.

    Comme Paul Von le souligne, ceci est corrigé en retournant le contenu de la cellule au lieu de la cellule entière.

    Cependant, comme le souligne Hons, un appui long sur l’en-tête de la section provoque l’application de l’application.

    La solution consiste à supprimer tous les gestureRecognizers de contentView.

     -(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSSsortingng *CellIdentifier = @"SectionHeader"; UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; while (sectionHeaderView.contentView.gestureRecognizers.count) { [sectionHeaderView.contentView removeGestureRecognizer:[sectionHeaderView.contentView.gestureRecognizers objectAtIndex:0]]; } return sectionHeaderView.contentView; } 

    Si vous n’utilisez pas de gestes dans les vues de votre en-tête de section, ce petit hack semble le faire.

    Si vous utilisez des storyboards, vous pouvez utiliser une cellule prototype dans la tableview pour mettre en forme votre vue d’en-tête. Définissez un identifiant unique et viewForHeaderInSection vous pouvez désinstaller la cellule avec cet ID et la convertir en UIView.

    La solution que j’ai proposée est fondamentalement la même que celle utilisée avant l’introduction des storyboards.

    Créez un nouveau fichier de classe d’interface vide. Faites glisser un UIView sur le canevas, mise en page souhaitée.

    Chargez la pointe manuellement, affectez-la à la section d’en-tête / pied de page appropriée dans les méthodes de délégué viewForHeaderInSection ou viewForFooterInSection.

    J’espérais qu’Apple simplifiait ce scénario avec des storyboards et continuait à chercher une solution meilleure ou plus simple. Par exemple, les en-têtes et les pieds de tableau personnalisés sont faciles à append.

    Si vous avez besoin d’une implémentation rapide, suivez les instructions de la réponse acceptée, puis dans UITableViewController implémentez les méthodes suivantes:

     override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let cell = self.tableView.dequeueReusableCell(withIdentifier: "CustomHeader") as! CustomHeaderUITableViewCell return cell } override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 75 } 

    Lorsque vous retournez le contenu de la cellule, vous aurez 2 problèmes:

    1. crash lié aux gestes
    2. vous ne réutilisez pas contentView (chaque fois que viewForHeaderInSection appelez viewForHeaderInSection , vous créez une nouvelle cellule)

    Solution:

    Classe Wrapper pour en-tête de table \ footer. C’est juste un conteneur, hérité de UITableViewHeaderFooterView , qui contient la cellule à l’intérieur

    https://github.com/Magnat12/MGTableViewHeaderWrapperView.git

    Enregistrez la classe dans votre UITableView (par exemple, dans viewDidLoad)

     - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:[MGTableViewHeaderWrapperView class] forHeaderFooterViewReuseIdentifier:@"ProfileEditSectionHeader"]; } 

    Dans votre UITableViewDelegate:

     - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { MGTableViewHeaderWrapperView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"ProfileEditSectionHeader"]; // init your custom cell ProfileEditSectionTitleTableCell *cell = (ProfileEditSectionTitleTableCell * ) view.cell; if (!cell) { cell = [tableView dequeueReusableCellWithIdentifier:@"ProfileEditSectionTitleTableCell"]; view.cell = cell; } // Do something with your cell return view; } 

    J’avais l’habitude de faire les choses suivantes pour créer des vues d’en-tête / pied de page paresseusement:

    • Ajouter un contrôleur de vue libre pour l’en-tête de section / pied de page au storyboard
    • Gérer tous les éléments de l’en-tête dans le contrôleur de vue
    • Dans la vue de table, le contrôleur fournit un tableau modifiable de contrôleurs de vue pour les en-têtes / pieds de page repeuplés avec [NSNull null]
    • Dans viewForHeaderInSection / viewForFooterInSection si le contrôleur de vue n’existe pas encore, créez-le avec les storyboards instantiateViewControllerWithIdentifier, mémorisez-le dans le tableau et renvoyez la vue des contrôleurs de vue

    Vous devez utiliser la solution de Tieme en tant que base, mais oubliez le viewWithTag: et d’autres approches de poisson, essayez plutôt de recharger votre en-tête (en rechargeant cette section).

    Donc, après avoir installé votre vue d’en-tête de cellule personnalisée avec tous les éléments de mise en forme AutoLayout , il suffit de la désattendre et de renvoyer le contenu après votre configuration, comme:

     -(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSSsortingng *CellIdentifier = @"SectionHeader"; SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; sectionHeaderCell.myPrettyLabel.text = @"Greetings"; sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent return sectionHeaderCell.contentView; } 

    Pour faire suite à la suggestion de Damon , voici comment j’ai pu sélectionner l’en-tête comme une ligne normale avec un indicateur de divulgation.

    J’ai ajouté un bouton sous-classé d’UIButton (nom de sous-classe “ButtonWithArgument”) à la cellule prototype de l’en-tête et supprimé le texte du titre (le texte “Titre” en gras est un autre UILabel dans la cellule prototype)

    Button In Interface Builder

    puis réglez le bouton sur la vue entière de l’en-tête et ajoutez un indicateur de divulgation avec l’astuce d’ Avario

     - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { static NSSsortingng *CellIdentifier = @"PersonGroupHeader"; UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if(headerView == nil) { [NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSSsortingng ssortingngWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]]; } // https://stackoverflow.com/a/24044628/3075839 while(headerView.contentView.gestureRecognizers.count) { [headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]]; } ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4]; button.frame = headerView.bounds; // set tap area to entire header view button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass [button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside]; // https://stackoverflow.com/a/20821178/3075839 UITableViewCell *disclosure = [[UITableViewCell alloc] init]; disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator; disclosure.userInteractionEnabled = NO; disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px (button.bounds.size.height - 20) / 2, 20, 20); [button addSubview:disclosure]; // configure header title text return headerView.contentView; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { return 35.0f; } -(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer; { NSLog(@"header tap"); NSInteger section = ((NSNumber *)sender.argument).integerValue; // do something here } 

    ButtonWithArgument.h

     #import  @interface ButtonWithArgument : UIButton @property (nonatomic, strong) NSObject *argument; @end 

    ButtonWithArgument.m

     #import "ButtonWithArgument.h" @implementation ButtonWithArgument @end 

    J’ai eu des problèmes dans un scénario où Header n’a jamais été réutilisé même en faisant toutes les étapes appropriées.

    Donc, comme une note de conseil à tous ceux qui veulent atteindre la situation de montrer des sections vides (0 lignes), soyez averti que:

    dequeueReusableHeaderFooterViewWithIdentifier ne réutilise pas l’en-tête tant que vous n’avez pas renvoyé au moins une ligne

    J’espère que cela aide

    Qu’en est-il d’une solution où l’en-tête est basé sur un tableau de vue:

     class myViewController: UIViewController { var header: [UILabel] = mySsortingngArray.map { (thisTitle: Ssortingng) -> UILabel in let headerView = UILabel() headerView.text = thisTitle return(headerView) } 

    Suivant dans le délégué:

     extension myViewController: UITableViewDelegate { func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { return(header[section]) } } 

    Voici la réponse de @Vitaliy Gozhenko , dans Swift.
    Pour résumer, vous allez créer un UITableViewHeaderFooterView qui contient un UITableViewCell. Cet UITableViewCell sera “désinstallable” et vous pourrez le concevoir dans votre storyboard.

    1. Créez une classe UITableViewHeaderFooterView

       class CustomHeaderFooterView: UITableViewHeaderFooterView { var cell : UITableViewCell? { willSet { cell?.removeFromSuperview() } didSet { if let cell = cell { cell.frame = self.bounds cell.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth] self.contentView.backgroundColor = UIColor .clearColor() self.contentView .addSubview(cell) } } } 
    2. Branchez votre tableview avec cette classe dans votre fonction viewDidLoad:

       self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID") 
    3. Lorsque vous demandez un en-tête de section, désélectionnez un CustomHeaderFooterView et insérez une cellule

       func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let view = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("SECTION_ID") as! CustomHeaderFooterView if view.cell == nil { let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell") view.cell = cell; } // Fill the cell with data here return view; } 
    1. Ajouter une cellule dans StoryBoard et définir la reuseidentified

      sb

    2. Code

       class TP_TaskViewTableViewSectionHeader: UITableViewCell{ } 

      et

      lien

    3. Utilisation:

       func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section)) return header }