Modifier le comportement de défilement par défaut de l'en-tête de section UITableView

147

J'ai un UITableView avec deux sections. C'est une vue de table simple. J'utilise viewForHeaderInSection pour créer des vues personnalisées pour ces en-têtes. Jusqu'ici tout va bien.

Le comportement de défilement par défaut est que lorsqu'une section est rencontrée, l'en-tête de section reste ancré sous la barre de navigation, jusqu'à ce que la section suivante défile dans la vue.

Ma question est la suivante: puis-je changer le comportement par défaut de sorte que les en-têtes de section ne restent PAS ancrés en haut, mais plutôt défilent sous la barre de navigation avec le reste des lignes de section?

Est-ce que je rate quelque chose d'évident?

Merci.

Davidjhinson
la source
1
Cela doit être l'une des meilleures questions écrites sur ce site! :) Merci.
Fattie
1
Vérifiez ici la bonne façon d'obtenir cet effet stackoverflow.com/a/13735238/1880899
Sumit Anantwar

Réponses:

175

La façon dont j'ai résolu ce problème est d'ajuster le en contentOffsetfonction du contentInsetdans UITableViewControllerDelegate(étend UIScrollViewDelegate) comme ceci:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
       CGFloat sectionHeaderHeight = 40;
   if (scrollView.contentOffset.y<=sectionHeaderHeight&&scrollView.contentOffset.y>=0) {
       scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 0, 0, 0);
   } else if (scrollView.contentOffset.y>=sectionHeaderHeight) {
       scrollView.contentInset = UIEdgeInsetsMake(-sectionHeaderHeight, 0, 0, 0);
   }
}

Le seul problème ici est qu'il perd un peu de rebond lors du défilement vers le haut.


{REMARQUE: Le "40" doit être la hauteur exacte de votre en-tête de section 0. Si vous utilisez un nombre plus grand que la hauteur de votre en-tête de section 0, vous verrez que la sensation du doigt est affectée (essayez comme "1000" et vous verrez que le comportement de rebond est un peu bizarre en haut). si le nombre correspond à la hauteur de l'en-tête de la section 0, la sensation du doigt semble parfaite ou presque parfaite.}

awulf
la source
1
Solution parfaite que celle ci-dessus.
prathumca
4
Je vous remercie! C'est une solution parfaite. Avez-vous la solution pour le pied de page de la section?
Tuan Nguyen
12
Cette solution ne gère pas correctement la barre de menus tap-on, qui est censée défiler vers le haut de la liste.
Reid Ellis
si vous avez besoin du même comportement pour la vue du pied de page de la section, changez simplement la dernière ligne comme ceci: scrollView.contentInset = UIEdgeInsetsMake (0, 0, sectionHeaderHeight, 0)
alex
Cela semble être suffisant: scrollView.contentInset = UIEdgeInsetsMake (scrollView.contentOffset.y> = 0? -ScrollView.contentOffset.y: 0, 0, 0, 0);
MacMark
85

Vous pouvez également ajouter une section avec zéro ligne en haut et simplement utiliser le pied de page de la section précédente comme en-tête pour la suivante.

voidStern
la source
13
Dans mon cas où j'ai plus de 2 sections, le pied de page s'ancrera en bas ..
Joseph Lin
3
C'est créatif, mais vous devez ajuster l'indexPath si vous utilisez un NSFetchedResultsController pour charger la table.
XJones
+1 de loin la MEILLEURE solution - m'a pris comme 3 lignes à mettre en œuvre!
Jon
Brilliant Brilliant Brilliant :-)
iphonic
Comment cela fonctionnerait-il pour le pied de page qui ancre? J'apprécierais de l'aide! Merci
darwindeeds
36

Si c'était moi qui faisais cela, je profiterais du fait que UITableViews dans le style Plain a les en-têtes collants et ceux dans le style Grouped n'en ont pas. J'essaierais probablement au moins d'utiliser une cellule de tableau personnalisée pour imiter l'apparence des cellules simples dans un tableau groupé.

Je n'ai pas vraiment essayé cela, donc cela peut ne pas fonctionner, mais c'est ce que je suggérerais de faire.

Colin Barrett
la source
Cela fonctionnerait probablement, mais a-t-il été essayé? Et cela semble beaucoup de travail quand la réponse de @ voidStern fonctionne parfaitement !!
bentford
La réponse de voidStern ne fonctionne pas pour moi lorsque j'ai plus de deux sections. La réponse de Colin est plus simple / plus élégante, sans avoir besoin de changer manuellement le numéro de section et d'ajouter le nombre de sections.
Joseph Lin
Conseils: utilisez tableView.separatorColor = [UIColor clearColor];pour imiter une vue de tableau simple.
Joseph Lin
3
J'ai essayé de faire cela, mais quand je ne pouvais pas faire ressembler les cellules du tableau groupé aux simples, c'est-à-dire supprimer les marges gauche et droite de chaque côté des cellules. Comment avez-vous fait cela?
Adam Carter
1
Merci beaucoup d'avoir clarifié les choses à propos de UITableViewStyle, c'est-à-dire groupé et simple, qui décide si l'en-tête / pied de page de la section tableview sera collant ou non. Merci beaucoup @Colin Barrett
Dhaval H.Nena
28

Je sais qu'il arrive tard, mais j'ai trouvé la solution définitive!

Ce que vous voulez faire est si vous avez 10 sections, laissez la source de données renvoyer 20. Utilisez des nombres pairs pour les en-têtes de section et des nombres impairs pour le contenu de section. quelque chose comme ça

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (section%2 == 0) {
        return 0;
    }else {
        return 5;
    }
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    if (section%2 == 0) {
        return [NSString stringWithFormat:@"%i", section+1];
    }else {
        return nil;
    }
}

Voilá! :RÉ

LocoMike
la source
bonne idée. mais si vous avez des titres d'index de section, je soupçonne que cela les gâchera.
roocell
Pourquoi? vous pouvez faire de même avec les titres de section. Souvenez-vous simplement de zéro pour les nombres impairs et d'un titre pour le nombre pair.
LocoMike
1
oui - cette méthode fonctionne très bien. C'était beaucoup de comptabilité (probablement à voir avec la façon dont je dois obtenir les titres des sections) mais cela a fonctionné. L'astuce est que numberOfRowsInSection et cellForRowAtIndexPath utilisent if (section% 2! = 0 && section! = 0) tandis que titleForHeaderInSection et les autres fonctions d'index de section utilisent if (section% 2 == 0)
roocell
l'expéditeur doit choisir ceci comme réponse. Ce serait génial s'il y avait un moyen de sous-classer uitableview pour ancrer automatiquement les titres de section.
roocell
Cela a fonctionné le mieux pour moi. J'ai d'abord essayé la solution de @ Colin, mais cela ne fonctionnait pas bien avec le très personnalisé UITableViewque j'utilise.
kubi
15

Il y a plusieurs choses à faire pour résoudre ce problème de manière non hacky:

  1. Définissez le style de vue de table sur UITableViewStyleGrouped
  2. Réglez l'affichage du tableau backgroundColorde[UIColor clearColor]
  3. Définissez la backgroundViewcellule de chaque vue de tableau sur une vue vide avecbackgroundColor [UIColor clearColor]
  4. Si nécessaire, définissez la vue de la table de rowHeightmanière appropriée ou remplacez-la tableView:heightForRowAtIndexPath:si les lignes individuelles ont des hauteurs différentes.
Neil Gall
la source
(+1) @Neil, j'ai essayé votre réponse mais malheureusement, pour UITableViewStyleGroupedles cellules du tableau ont des marges supplémentaires qui changent leur alignement avec l'en-tête. Il s'agit d'un problème lorsque vous souhaitez réellement représenter une table à plusieurs colonnes et utiliser l'en-tête pour afficher les titres des colonnes (puis aligner les entrées de table individuelles sur ces titres).
brainjam
15

Publié à l'origine ici , une solution rapide utilisant l'IB. La même chose peut être faite par programme mais tout simplement.

Un moyen probablement plus simple d'y parvenir (en utilisant IB):

Faites glisser un UIView sur votre TableView pour en faire sa vue d'en-tête.

  1. Définissez la hauteur de la vue d'en-tête sur 100 px
  2. Définissez le paramètre tableview contentInset (top) sur -100
  3. Les en-têtes de section défileront désormais comme n'importe quelle cellule normale.

Certaines personnes ont commenté en disant que cette solution cache le premier en-tête, mais je n'ai remarqué aucun problème de ce type. Cela a parfaitement fonctionné pour moi et était de loin la solution la plus simple que j'ai vue jusqu'à présent.

Doc
la source
Notez que vous devriez probablement définir la vue pour utiliser une couleur d'arrière-plan de couleur claire, sinon si vous faites défiler le haut, vous voyez du blanc dans le rebond.
Doc
3
Il cache mon premier en-tête
Leena
Leena, quelle version d'iOS utilisez-vous? La vue est-elle un UITableViewController ou un UIView avec un UITableView? Je ne sais pas pourquoi pour certaines personnes, cette solution masque le premier en-tête, alors j'essaie de trouver des divergences.
Doc
Cette solution est parfaite. Je recherche une apparence d'arrière-plan de cellule supérieure et inférieure infinie - cela l'accomplit totalement. Agréable!
Taylor Halliday
Cette solution est excellente. Plus facile à accomplir que les autres réponses de ce fil. À mon avis, cela devrait être la réponse acceptée.
John Rogers
15

Je n'étais pas satisfait des solutions décrites ici jusqu'à présent, j'ai donc essayé de les combiner. Le résultat est le code suivant, inspiré de @awulf et @cescofry. Cela fonctionne pour moi car je n'ai pas d'en-tête de vue tableau réel. Si vous disposez déjà d'un en-tête de vue tableau, vous devrez peut-être ajuster la hauteur.

// Set the edge inset
self.tableView.contentInset = UIEdgeInsetsMake(-23.0f, 0, 0, 0);

// Add a transparent UIView with the height of the section header (ARC enabled)
[self.tableView setTableHeaderView:[[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 23.0f)]];
Thomas Kekeisen
la source
C'était la solution la plus simple que j'ai trouvée.
Zorayr
Cela a fonctionné pour moi sur iOS9.2, donc même si une réponse plus ancienne, elle est toujours valide. Et cela n'a pas masqué le premier en-tête de section. Fonctionne et fonctionne parfaitement.
idStar
Parfait. Si votre tableview a déjà un en-tête, augmentez simplement sa hauteur du décalage, puis augmentez la constante de contrainte supérieure du contenu de l'en-tête pour déplacer le contenu vers la position d'origine
Jason
14

Changez simplement le style de TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

Documentation UITableViewStyle :

UITableViewStylePlain - Une vue de table simple. Tous les en-têtes ou pieds de page de section sont affichés sous forme de séparateurs en ligne et flottent lorsque la vue de tableau est défilée.

UITableViewStyleGrouped - Une vue de table dont les sections présentent des groupes distincts de lignes. Les en-têtes et pieds de page de section ne flottent pas.

Arthur S
la source
excellente réponse, a fonctionné pour moi. pas besoin de travailler autour
Shams Ahmed
9

Sélectionnez le style TableView groupée

Sélectionnez le style de vue de tableau groupé dans l'inspecteur d'attributs de votre tableView dans le storyboard.

Arjun Shukla
la source
5

Définissez l'en-têteView du tableau avec une vue transparente avec la même hauteur de l'en-tête en vue en coupe. Lancez également la vue de table avec une image à la hauteur.

self.tableview = [[UITableView alloc] initWithFrame:CGRectMake(0, - height, 300, 400)];

UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)] autorelease];
[self.tableView setTableHeaderView:headerView];
cescofry
la source
4

J'ai trouvé une solution alternative, utilisez la première cellule de chaque section à la place d'une vraie section d'en-tête, cette solution ne semble pas si propre, mais fonctionne si bien, vous pouvez utiliser une cellule prototype définie pour votre section d'en-têtes, et dans la méthode cellForRowAtIndexPath demandez l'indexPath.row == 0, si vrai, utilisez la cellule prototype de la section d'en-tête, sinon utilisez votre cellule prototype par défaut.

AlexBB
la source
Si vous souhaitez toujours travailler avec indexPath.row& indexPath.section, assurez-vous de renvoyer une hauteur de 0.0ffor - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)sectionpour que vos en-têtes soient invisibles.
Soupe
4

Changez votre style TableView:

self.tableview = [[UITableView alloc] initwithFrame:frame style:UITableViewStyleGrouped];

Selon la documentation Apple pour UITableView:

UITableViewStylePlain - Une vue de table simple. Tous les en-têtes ou pieds de page de section sont affichés sous forme de séparateurs en ligne et flottent lorsque la vue de tableau est défilée.

UITableViewStyleGrouped - Une vue de table dont les sections présentent des groupes distincts de lignes. Les en-têtes et pieds de page de section ne flottent pas. J'espère que ce petit changement vous aidera.

Aks
la source
2

Maintenant que le style groupé ressemble fondamentalement au style simple dans iOS 7 (en termes de planéité et d'arrière-plan), pour nous, la solution la meilleure et la plus simple (c'est-à-dire la moins hacky) était de simplement changer le style de la vue de tableau en groupé. Jacking avec contentInsets a toujours été un problème lorsque nous avons intégré une barre de navigation déroulante en haut. Avec un style de vue tableau groupé, il a exactement la même apparence (avec nos cellules) et les en-têtes de section restent fixes. Aucune bizarrerie de défilement.

Greg Combs
la source
C'est la meilleure solution surtout si les cellules sont personnalisées, cela semble simplement arrêter le "maintien" de l'en-tête de section lorsque le défilement atteint le haut.
Acier recyclé
1

Attribuez un insert négatif à votre tableView. Si vous avez des en-têtes de section haute de 22 pixels et que vous ne voulez pas qu'ils soient collants, juste après avoir rechargé les données, ajoutez:

self.tableView.contentInset = UIEdgeInsetsMake(-22, 0, 0, 0); 
self.tableView.contentSize = CGSizeMake(self.tableView.contentSize.width, self.tableView.contentSize.height+22); 

Fonctionne comme un charme pour moi. Fonctionne également pour les pieds de page, attribuez simplement l'encart négatif en bas à la place.

Mike A
la source
Cela coupe juste le premier en-tête ??
cannyboy
A parfaitement fonctionné pour moi !! Merci
Daniel
0

J'ajoute le tableau à une vue de défilement et cela semble bien fonctionner.

Dolbex
la source
0

Vérifiez ma réponse ici . C'est le moyen le plus simple d'implémenter les en-têtes de section non flottants sans aucun piratage.

Sumit Anantwar
la source
0

La réponse de @ LocoMike correspondait le mieux à ma tableView, mais elle a également cassé lors de l'utilisation des pieds de page. Voici donc la solution corrigée lors de l'utilisation d'en-têtes et de pieds de page:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return (self.sections.count + 1) * 3;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    if (section % 3 != 1) {
        return 0;
    }
    section = section / 3;
    ...
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return 0;
    }
    section = section / 3;
    ...
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return 0;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if (section % 3 != 0) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    if (section % 3 != 2) {
        return nil;
    }
    section = section / 3;
    ...
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    int section = indexPath.section;
    section = section / 3;
    ...
}
marmor
la source
0

Version rapide de @awulf answer, qui fonctionne très bien!

func scrollViewDidScroll(scrollView: UIScrollView) {
    let sectionHeight: CGFloat = 80
    if scrollView.contentOffset.y <= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake( -scrollView.contentOffset.y, 0, 0, 0)
    }else if scrollView.contentOffset.y >= sectionHeight {
        scrollView.contentInset = UIEdgeInsetsMake(-sectionHeight, 0, 0, 0)
    }
}
Jeff
la source
-2

J'ai appris qu'il suffit de définir la propriété tableHeaderView, c'est-à-dire:

 tableView.tableHeaderView = customView;

et c'est tout.

Soni
la source