Comment implémenter des en-têtes et des pieds de page de section de vue de table personnalisée avec Storyboard

176

Sans utiliser de storyboard, nous pourrions simplement faire glisser un UIViewsur le canevas, le mettre en page, puis le définir dans les méthodes tableView:viewForHeaderInSectionou tableView:viewForFooterInSectiondéléguer.

Comment y parvenir avec un StoryBoard où nous ne pouvons pas faire glisser un UIView sur le canevas

Seamus
la source

Réponses:

92

Je sais que cette question concernait iOS 5, mais pour le bénéfice des futurs lecteurs, notez que l'iOS 6 efficace que nous pouvons maintenant utiliser à la dequeueReusableHeaderFooterViewWithIdentifierplace dequeueReusableCellWithIdentifier.

Alors viewDidLoad, appelez soit registerNib:forHeaderFooterViewReuseIdentifier:ou registerClass:forHeaderFooterViewReuseIdentifier:. Ensuite viewForHeaderInSection, appelez tableView:dequeueReusableHeaderFooterViewWithIdentifier:. Vous n'utilisez pas de prototype de cellule avec cette API (il s'agit d'une vue basée sur NIB ou d'une vue créée par programme), mais il s'agit de la nouvelle API pour les en-têtes et pieds de page retirés de la file d'attente.

Rob
la source
13
soupir Une honte que ces deux réponses claires sur l'utilisation d'un NIB au lieu d'une proto cell soient actuellement sous 5 votes, et la réponse "hack it with proto cells" est au-dessus de 200.
Benjohn
5
La différence est qu'avec le hack de Tieme, vous pouvez créer votre design dans le même storyboard et ne pas utiliser un Xib séparé
CedricSoubrie
Pouvez-vous en quelque sorte éviter d'utiliser un fichier NIB séparé et utiliser un storyboard à la place?
Foriger
@Foriger - Pas pour le moment. C'est une étrange omission dans les vues de table de storyboard, mais c'est ce que c'est. Utilisez ce hack kludgy avec des cellules prototypes si vous le souhaitez, mais personnellement, je viens de supporter l'ennui des NIB pour les vues d'en-tête / pied de page.
Rob
2
Dire simplement qu'il est «vieux» n'est pas assez fort, à mon humble avis. La réponse acceptée est un moyen déroutant de contourner le fait que vous ne pouvez pas avoir de cellules prototypes pour les vues d'en-tête et de pied de page. Mais je pense que c'est tout simplement faux, car cela suppose que la suppression de la file d'attente des en-têtes et des pieds de page fonctionne de la même manière que la suppression de la file d'attente des cellules (ce que la présence d'une nouvelle API pour les en-têtes / pieds de page suggère que ce n'est peut-être pas le cas). La réponse acceptée est une solution de contournement créative qui était compréhensible dans iOS 5, mais, dans iOS 6 et versions ultérieures, il est préférable d'utiliser l'API explicitement conçue pour la réutilisation des en-têtes / pieds de page.
Rob
385

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

  • ajoutez une cellule supplémentaire et mettez-y les éléments souhaités.
  • définir l'identifiant sur quelque chose de spécifique (dans mon cas SectionHeader)
  • implémenter la tableView:viewForHeaderInSection:méthode ou la tableView:viewForFooterInSection:méthode
  • utiliser [tableView dequeueReusableCellWithIdentifier:]pour obtenir l'en-tête
  • mettre en œuvre la tableView:heightForHeaderInSection:méthode.

(voir capture d'écran)

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 numéro spécifique (par exemple 123)
  3. Dans votre tableView:viewForHeaderInSection:méthode, obtenez l'étiquette en appelant:
  1. Vous pouvez maintenant utiliser l'étiquette pour définir un nouveau titre:
Attache-moi
la source
7
En retard à la fête, mais pour désactiver les clics sur la vue d'en-tête de section, retournez simplement cell.contentView dans la viewForHeaderInSection méthode (pas besoin d'ajouter des UIViews personnalisées).
paulvs
3
@PaulVon J'ai essayé de le faire dans mon dernier projet, mais si vous faites cela, essayez simplement d'appuyer longuement sur l'un de vos en-têtes et il plantera
Hons
13
Dans iOS 6.0, dequeueReusableHeaderFooterViewWithIdentifierest introduit et est maintenant préféré à cette réponse. Mais l'utiliser correctement nécessite maintenant plus d'étapes. J'ai le guide @ samwize.com/2015/11/06/… .
samwize
3
Ne créez pas votre sectionHeaderViews en utilisant UITableViewCells, cela créera un comportement inattendu. Utilisez plutôt un UIView.
AppreciateIt
4
Veuillez ne jamais l'utiliser UITableViewCellcomme vue d'en-tête. Il vous sera très difficile de déboguer les problèmes visuels - l'en-tête disparaîtra parfois à cause de la façon dont les cellules sont retirées de la file d'attente et vous chercherez pendant des heures pourquoi jusqu'à ce que vous réalisiez qu'il UITableViewCelln'appartient pas à l'en- UITableViewtête.
raven_raven
53

Dans iOS 6.0 et supérieur, les choses ont changé avec la nouvelle dequeueReusableHeaderFooterViewWithIdentifierAPI.

J'ai rédigé un guide (testé sur iOS 9), qui peut se résumer ainsi:

  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 / pied de page
  3. Enregistrez la plume dans viewDidLoad
  4. Implémenter viewForHeaderInSectionet utiliser dequeueReusableHeaderFooterViewWithIdentifierpour récupérer l'en-tête / pied de page
samwize
la source
2
Merci pour cette réponse qui n'est PAS un hack, et la bonne façon de faire les choses. Merci également de m'avoir présenté UITableViewHeaderFooterVIew. Btw, le guide est tout simplement excellent! :)
Roboris
Bienvenue. La mise en œuvre d'un en-tête / pied de page pour une vue de table ou de collection n'est pas très bien documentée par Apple. Hier encore, j'étais coincé dans l'en-tête de lecture lors de la conception via storyboad .
samwize le
1
Cela devrait être la réponse la mieux notée à coup sûr!
Ben Williams
Pourriez-vous expliquer comment le faire via storyboard, au lieu de xib? Ce faisant, j'ai réussi à sortir de la file d'attente, mais mes UILabels sont nuls.
bunkerdive le
22

Je l'ai fait fonctionner dans iOS7 en utilisant une cellule prototype dans le storyboard. J'ai un bouton dans ma vue d'en-tête de section personnalisée qui déclenche une séquence configurée dans le storyboard.

Commencez avec la solution de Tieme

Comme le souligne pedro.m, le problème avec ceci est que le fait d'appuyer sur l'en-tête de section provoque la sélection de la première cellule de la section.

Comme le souligne Paul Von, cela est résolu en renvoyant le contentView de la cellule au lieu de la cellule entière.

Cependant, comme le souligne Hons, un appui long sur ledit en-tête de section plantera l'application.

La solution consiste à supprimer tous les gestureRecognizers de contentView.

Si vous n'utilisez pas de gestes dans vos vues d'en-tête de section, ce petit hack semble le faire.

Damon
la source
2
Tu as raison. Je viens de tester cela et son fonctionnement, j'ai donc mis à jour mon post ( hons82.blogspot.it/2014/05/uitableviewheader-done-right.html ) contenant toutes les solutions possibles que j'ai trouvées au cours de mes recherches. Merci beaucoup pour ce correctif
Hons
Bonne réponse ... Thanks Man
Jogendra.Com
13

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

barksten
la source
12

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

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    return tableView.dequeueReusableCell(withIdentifier: "CustomHeader")
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}
JZ.
la source
2
Pour une raison quelconque, cela n'a pas fonctionné pour moi jusqu'à ce que j'aie remplacé heightForHeaderInSection également.
Entalpi
C'est un code assez ancien. vous devriez poster une autre réponse!
JZ.
J'étais coincé parce que je ne savais pas que je devrais remplacer heightForHeaderInSection. Merci @Entalpi
guijob
1
@JZ. Dans votre exemple Swift 3, vous utilisez les self.tableView.dequeueReusableHeaderFooterView et Swift 2 suivants: self.tableView.dequeueReusableCellWithIdentifier y a-t-il une différence?
Vrutin Rathod
1
Cela a sauvé mes fesses! l'ajout d'une hauteur à headerCell le rend visible.
CRey
9

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

Créez un nouveau fichier de classe d'interface vide. Faites glisser un UIView sur la toile, mise en page comme vous le souhaitez.

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

J'avais espéré 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 pieds de page de tableau personnalisés sont faciles à ajouter.

Seamus
la source
Eh bien, Apple l'a fait si vous utilisez la cellule de storyboard comme méthode d'en-tête :) stackoverflow.com/questions/9219234/#11396643
Tieme
2
Sauf que cela ne fonctionne que pour les cellules prototypes, pas pour les cellules statiques.
Jean-Denis Muys
1
Une solution vraiment simple consiste à utiliser Pixate ; vous pouvez faire beaucoup de personnalisation sans obtenir une référence à l'en-tête. Tout ce que vous avez à mettre en œuvre est tableView:titleForHeaderInSection, qui est une ligne unique.
John Starr Dewar
5
C'est la vraie solution ... malheureusement ce n'est pas au top, donc il m'a fallu un certain temps pour le découvrir. J'ai résumé les problèmes que j'ai rencontrés hons82.blogspot.it/2014/05/uitableviewheader-done-right.html
Hons
C'est plus facile que cela, il suffit de glisser-déposer.
Ricardo
5

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

  1. crash lié aux gestes
  2. vous ne réutilisez pas contentView (à chaque viewForHeaderInSectionappel, vous créez une nouvelle cellule)

Solution:

Classe wrapper pour l'en-tête \ pied de page de la table. C'est juste un conteneur, hérité de UITableViewHeaderFooterView, qui contient la cellule

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

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

Dans votre UITableViewDelegate:

Vitaliy Gozhenko
la source
2

J'avais l'habitude de faire ce qui suit pour créer paresseusement des vues d'en-tête / pied de page:

  • Ajouter un contrôleur de vue de forme libre pour l'en-tête / pied de page de la section au storyboard
  • Gérez tous les éléments de l'en-tête dans le contrôleur de vue
  • Dans le contrôleur de vue de table, fournissez un tableau mutable de contrôleurs de vue pour les en-têtes / pieds de page de section 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
Vipera Berus
la source
2

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

Donc, comme une note de conseil à tous ceux qui veulent obtenir la situation de montrer les 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

Juan Pedro Lozano
la source
2

Similaire à laszlo answer mais vous pouvez réutiliser la même cellule prototype pour les cellules du tableau et la cellule d'en-tête de section. Ajoutez les deux premières fonctions ci-dessous à votre sous-classe UIViewController

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell") as! DataCell
    cell.data1Label.text = "DATA KEY"
    cell.data2Label.text = "DATA VALUE"
    return cell
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

// Example of regular data cell dataDelegate to round out the example
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as! PlayerCell

    cell.data1Label.text = "\(dataList[indexPath.row].key)"
    cell.data2Label.text = "\(dataList[indexPath.row].value)"
    return cell
}
Richard Legault
la source
1

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

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

Bouton dans le générateur d'interface

puis définissez le bouton sur la vue entière de l'en-tête et ajouté un indicateur de divulgation avec l'astuce d' Avario

ButtonWithArgument.h

ButtonWithArgument.m

MarkF
la source
1

Vous devriez utiliser la solution de Tieme comme base, mais oubliez les viewWithTag:approches louche et les autres, 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 AutoLayoutéléments sophistiqués , retirez-la simplement de la file d'attente et renvoyez le contentView après votre configuration, comme:

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

    SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    sectionHeaderCell.myPrettyLabel.text = @"Greetings";
    sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent

    return sectionHeaderCell.contentView;
}  
Laszlo
la source
1

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

class myViewController: UIViewController {
    var header: [UILabel] = myStringArray.map { (thisTitle: String) -> 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])
    }
}
CyrIng
la source
1
  1. Ajouter une cellule StoryBoardet définirreuseidentified

    qn

  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
    }
    
leo.tan
la source
2
Ce n'est pas la bonne façon d'ajouter un en-tête
Manish Malviya
2
alors quel est le chemin?
Pramod Shukla
0

Voici la réponse de @Vitaliy Gozhenko , dans Swift.
Pour résumer, vous allez créer un UITableViewHeaderFooterView qui contient un UITableViewCell. Ce UITableViewCell sera "retirable" et vous pourrez le concevoir dans votre storyboard.

  1. Créer 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, retirez un CustomHeaderFooterView de la file d'attente et insérez-y 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;
    }
    
CédricSoubrie
la source