Sans utiliser de story-board, nous pourrions simplement faire glisser une UIView
sur le canevas, la disposer puis la 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 UIView sur la toile
Je sais que cette question était pour iOS 5, mais pour le bénéfice des futurs lecteurs, notez qu'effectivement 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 de prototype de cellule avec cette API (c'est une vue basée sur une carte NIB ou une vue créée par programme), mais il s'agit de la nouvelle API pour les en-têtes et les pieds de page supprimés.
Utilisez simplement une cellule prototype comme en-tête et/ou pied de section.
tableView:viewForHeaderInSection:
ou la méthode tableView:viewForFooterInSection:
[tableView dequeueReusableCellWithIdentifier:]
pour obtenir l'en-têtetableView:heightForHeaderInSection:
.-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *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;
}
Modifier: Comment changer le titre de l'en-tête (question commentée):
tableView:viewForHeaderInSection:
, obtenez l'étiquette en appelant: UILabel *label = (UILabel *)[headerView viewWithTag:123];
[label setText:@"New Title"];
Dans iOS 6.0 et les versions ultérieures, 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:
UITableViewHeaderFooterView
viewDidLoad
viewForHeaderInSection
et utilisez dequeueReusableHeaderFooterViewWithIdentifier
pour récupérer l'en-tête/le pied de pageJe l'ai fait fonctionner dans iOS7 en utilisant un prototype de cellule 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 est que, si vous appuyez sur l'en-tête de la section, la première cellule de la section est sélectionnée.
Comme le souligne Paul Von, le problème est résolu en renvoyant le contenu de la cellule à la place de la totalité de la cellule.
Cependant, comme le souligne Hons, un appui long sur l'en-tête de cette section plantera l'application.
La solution consiste à supprimer tous les gestureRecognizers de contentView.
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
static NSString *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 vos vues d'en-tête de section, ce petit bidouillage semble le faire.
Si vous utilisez des storyboards, vous pouvez utiliser une cellule prototype dans la vue tableau pour mettre en forme votre vue d'en-tête. Définissez un identifiant unique et viewForHeaderInSection que vous pouvez retirer de la cellule avec cet ID et le convertir en UIView.
Si vous avez besoin d’une implémentation rapide de cette opération, suivez les instructions de la réponse acceptée, puis appliquez UITableViewController aux 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
}
La solution que j'ai proposée est fondamentalement la même que celle utilisée avant l'introduction des scénarimages.
Créez un nouveau fichier de classe d'interface vide. Faites glisser un UIView sur le canevas, mettez-le comme vous le souhaitez.
Chargez le nib manuellement, attribuez-le à la section d’en-tête/pied de page appropriée dans les méthodes de délégation 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 de table et les pieds de page personnalisés sont faciles à ajouter.
Lorsque vous renvoyez le contenu de la cellule, vous aurez 2 problèmes:
viewForHeaderInSection
, vous créez une nouvelle cellule)Solution:
Classe wrapper pour l'en-tête de table\footer . Il ne s'agit que d'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;
}
Je faisais les choses suivantes pour créer des vues d'en-tête/pied de page paresseusement:
[NSNull null]
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.
En guise de conseil à tous ceux qui souhaitent atteindre la situation suivante, montrez les sections vides (0 lignes):
dequeueReusableHeaderFooterViewWithIdentifier ne réutilisera pas l'en-tête tant que vous n'aurez pas renvoyé au moins une ligne
J'espère que ça aide
Pour faire suite à la question de { suggestion de Damon }, voici comment j'ai sélectionné l'en-tête sélectionnable, tout comme une ligne normale avec un indicateur de divulgation.
J'ai ajouté un bouton sous-classé dans UIButton (nom de la sous-classe "ButtonWithArgument") dans 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 du prototype).
puis définissez le bouton sur l'intégralité de la vue en-tête et ajoutez un indicateur de divulgation avec astuce d'Avario
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *CellIdentifier = @"PersonGroupHeader";
UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(headerView == nil)
{
[NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"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 <UIKit/UIKit.h>
@interface ButtonWithArgument : UIButton
@property (nonatomic, strong) NSObject *argument;
@end
ButtonWithArgument.m
#import "ButtonWithArgument.h"
@implementation ButtonWithArgument
@end
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])
}
}
Vous devriez utiliser la solution de Tieme comme base, mais oubliez le viewWithTag:
et les autres approches ludiques, essayez plutôt de recharger votre en-tête (en rechargeant cette section).
Ainsi, une fois que vous avez créé votre vue personnalisée en-tête de cellule avec tous les éléments fantaisie AutoLayout
, il suffit de la retirer de la file d'attente et de renvoyer le contentView après votre configuration, comme suit:
-(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;
}
Ajouter une cellule dans StoryBoard
et définir reuseidentified
Code
class TP_TaskViewTableViewSectionHeader: UITableViewCell{
}
et
Utilisation:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section))
return header
}
Voici la réponse de @Vitaliy Gozhenko , dans Swift.
En résumé, vous allez créer un UITableViewHeaderFooterView contenant un UITableViewCell. Ce UITableViewCell sera "dequeuable" et vous pouvez le concevoir dans votre storyboard.
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)
}
}
}
Branchez votre tableview avec cette classe dans votre fonction viewDidLoad:
self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
Lorsque vous demandez un en-tête de section, supprimez le CustomHeaderFooterView 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;
}
Identique à laszlo answer, mais vous pouvez réutiliser le même prototype de cellule pour les cellules de 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
}