web-dev-qa-db-fra.com

UITableView avec des hauteurs de cellules dynamiques sautant lors du défilement après le rechargement d'une cellule

J'ai une vue sous forme de tableau avec le potentiel pour chaque cellule d'avoir sa propre hauteur, donc ne convient pas pour utiliser rowHeight. Au lieu de cela, j'utilise actuellement let indexSet = NSIndexSet(index: 10) et self.tableView.estimatedRowHeight = 75. Cela signifie qu'il appelle la fonction sizeThatFits sur la cellule pour déterminer sa hauteur. Tout cela fonctionne bien.

Le problème est alors lorsque vous rechargez une cellule qui est à l'écran. Faites défiler la liste vers le bas pour afficher la cellule 10, par exemple, puis rechargez la cellule 10, fonctionne bien. Mais lorsque vous commencez à remonter dans le champ, après les cellules que vous avez déjà vues, la valeur de valeur estimée pour RowReight est ignorée pour chaque cellule, sans tenir compte de la variable sizeThatFits. Il serait impossible pour moi de donner une estimation précise ou assez bonne de RowReight afin que ce saut ne soit pas perceptible, car mes cellules pourront afficher une ligne de texte ou une image complète, ce qui constitue une différence importante. Taille.

J'ai montré cet effet ici:

https://vid.me/edgW

J'ai fait de nombreuses tentatives différentes en combinant hauteurForRowAtIndexPath, estimationHeightForRowAtIndexPath .. etc. J'ai essayé divers conseils sur StackOverflow. Rien ne semble fonctionner.

J'ai joint un exemple de projet très simple, où vous pouvez l'essayer vous-même:

https://www.dropbox.com/s/8f1rvkx9k23q6c1/tableviewtest.zip?dl=0

  1. Exécutez le projet.
  2. Faites défiler jusqu'à ce que la cellule 10 soit visible.
  3. Attendez 5 secondes que la cellule se recharge (elle devient violette).
  4. Faites défiler vers le haut.

À noter - cela se produit {not} _ si la cellule n'est pas visible lorsqu'elle est rechargée. S'il se situe au-dessus de ou en dessous du point de défilement actuel, tout fonctionne comme prévu.

14
Andrew

Ce comportement semble être un bogue, ne serait-ce que pour reproduire cela sur iOS 9. Pour une autre raison, il n’est plus reproductible sur iOS 9. Je suis sûr que ce n’est pas très réconfortant.

Le problème provient principalement d’une estimation inexacte, comme @NickCatib a déclaré. Le mieux que vous puissiez faire sur iOS 8 est d’améliorer l’estimation. Une technique recommandée par de nombreux utilisateurs consiste à mettre en cache les hauteurs dans willDisplayCell et à les utiliser lors d'appels ultérieurs à estimatedRowHeightAtIndexPath.

Vous pourrez peut-être limiter le comportement en ne faisant rien pour que UITableView supprime ses caches, par exemple en modifiant le contenu d'une cellule directement à l'aide de cellForRowAtIndexPath plutôt qu'en utilisant le rechargement s'il est affiché à l'écran. Toutefois, cela ne vous aidera pas si vous devez réellement modifier la hauteur de la cellule.

J'ai peur de dire que le bogue ne peut pas être facilement corrigé dans une vue de table, car vous n'avez pas le contrôle de la mise en page. Le bogue peut être plus facilement contourné dans une sous-classe UICollectionViewFlowLayout en changeant la contentOffsetAdjustment lors de l'invalidation, bien que cela puisse ne pas être terriblement facile.

17
zwaldowski

J'avais aussi ce problème et utilisais une solution de SO que je suis incapable de trouver pour le moment. Si je le fais, je vais ajouter le lien. Voici la solution:

Le problème réside dans le fait que la vue tableau n'a pas l'estimation correcte pour la hauteur des lignes. Pour résoudre ce problème, mettez la hauteur en cache dans willDisplayCell et utilisez cette hauteur la prochaine fois.

Code

Dans viewDidLoad:

heightAtIndexPath = [NSMutableDictionary new];


- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    NSNumber *height = @(cell.frame.size.height);
    [heightAtIndexPath setObject:height forKey:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return UITableViewAutomaticDimension;
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
    if([heightAtIndexPath objectForKey:indexPath]) {
        return [[heightAtIndexPath objectForKey:indexPath] floatValue];
    } else {
        return UITableViewAutomaticDimension;
    }
}
10
Pranoy C

Euh ... c'est un peu difficile à régler.

Jetons un coup d'oeil à facebook. Ils ont eu le même problème avec leur calendrier, et ils ont fini par le faire dans une sorte de vue Web.

J'ai eu un problème similaire avec une sorte de calendrier, utilisé la hauteur de ligne automatique et eu ce problème. La première chose à faire était de régler estimatedHeight aussi près que possible de la hauteur moyenne des cellules. C'est assez difficile à gérer car vous pouvez avoir du texte (hauteur 50) ou des images + texte (hauteur 1500). La prochaine chose à faire avec l'amélioration de cette situation a été la mise en œuvre de estimatedHeight forIndexPath qui renvoie fondamentalement différentes hauteurs estimées pour différents indexPaths.

Après cela, il y avait beaucoup d'autres solutions, mais c'était aussi proche que possible avec des hauteurs variables (énormes différences).

0
Miknash