Est-il possible de mettre à jour la hauteur d'un seul UITableViewCell, sans recalculer la hauteur de chaque cellule?

J'ai un UITableView avec quelques sections différentes. Une section contient des cellules qui seront redimensionnées lorsqu'un user tape du text dans UITextView. Une autre section contient des cellules qui rendent un contenu HTML, pour lequel le calcul de la hauteur est relativement coûteux.

En ce moment, lorsque l'user tape dans l'UITextView, afin d'get la vue de la table pour mettre à jour la hauteur de la cellule, j'appelle

[self.tableView beginUpdates]; [self.tableView endUpdates]; 

Toutefois, cela entraîne la table à recalculer la hauteur de chaque cellule dans la table, lorsque j'ai vraiment seulement besoin de mettre à jour la cellule unique qui a été tapée dans. Non seulement cela, mais au lieu de recalculer la hauteur estimée en utilisant tableView:estimatedHeightForRowAtIndexPath: tableView:heightForRowAtIndexPath: elle appelle tableView:heightForRowAtIndexPath: pour chaque cellule, même celles qui ne sont pas affichées.

Est-il possible de requestr à la vue de table de mettre à jour juste la taille d'une seule cellule, sans faire tout ce travail inutile?

Mettre à jour

Je suis toujours à la search d'une solution à ce problème. Comme suggéré, j'ai essayé d'utiliser reloadRowsAtIndexPaths: mais cela ne semble pas fonctionner. Appeler reloadRowsAtIndexPaths: même avec une seule ligne, toujours provoquer heightForRowAtIndexPath: pour chaque ligne, même si cellForRowAtIndexPath: sera seulement appelé pour la ligne demandée. En fait, il semble que chaque fois qu'une ligne est insérée, supprimée ou rechargée, heightForRowAtIndexPath: est appelée pour chaque ligne de la cellule du tableau.

J'ai aussi essayé de mettre du code dans willDisplayCell:forRowAtIndexPath: pour calculer la hauteur juste avant qu'une cellule ne s'affiche. Pour que cela fonctionne, je devrais forcer la vue de table à re-requestr la hauteur pour la ligne après que je fasse le calcul. Malheureusement, en appelant [self.tableView beginUpdates]; [self.tableView endUpdates]; [self.tableView beginUpdates]; [self.tableView endUpdates]; from willDisplayCell:forRowAtIndexPath: provoque une exception d'index hors limites dans le code interne de UITableView. Je suppose qu'ils ne s'attendent pas à ce que nous fassions cela.

Je ne peux pas m'empêcher de penser que c'est un bug dans le SDK qui en réponse à [self.tableView endUpdates] estimatedHeightForRowAtIndexPath: pour les cellules qui ne sont pas visibles, mais j'essaie toujours de find une sorte de solution de contournement. Toute aide est appréciée.

Comme indiqué, reloadRowsAtIndexPaths:withRowAnimation: provoquera uniquement l'affichage de la table à son UITableViewDataSource pour une nouvelle vue de cellule mais ne requestra pas à UITableViewDelegate une hauteur de cellule mise à jour.

Malheureusement, la hauteur ne sera rafraîchie en appelant:

 [tableView beginUpdates]; [tableView endUpdates]; 

Même sans aucun changement entre les deux appels.

Si votre algorithm de calcul des hauteurs prend trop de time, vous devriez peut-être mettre ces valeurs en cache. Quelque chose comme:

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CGFloat height = [self cachedHeightForIndexPath:indexPath]; // Not cached ? if (height < 0) { height = [self heightForIndexPath:indexPath]; [self setCachedHeight:height forIndexPath:indexPath]; } return height; } 

Et assurez-vous de réinitialiser ces hauteurs à -1 lorsque le contenu change ou au moment de l'initialisation.

Modifier:

Aussi, si vous voulez retarder autant que possible le calcul de hauteur (jusqu'à ce que vous le faites défiler), essayez d'implémenter ceci (iOS 7+ uniquement):

@property (nonatomic) CGFloat estimatedRowHeight

Fournir une estimation non négative de la hauteur des lignes peut améliorer les performances de chargement de la vue tabulaire. Si la table contient des rangées de hauteur variables, il peut être coûteux de calculer toutes leurs hauteurs lors du chargement de la table. L'utilisation de l'estimation vous permet de reporter une partie du coût du calcul de la geometry du time de chargement au time de défilement.

La valeur par défaut est 0, ce qui signifie qu'il n'y a pas d'estimation.

Ce bug a été corrigé dans iOS 7.1.

Dans iOS 7.0, il ne semble pas y avoir moyen de contourner ce problème. L'appel de [self.tableView endUpdates] provoque l' heightForRowAtIndexPath: pour chaque cellule de la table.

Cependant, dans iOS 7.1, l'appel de [self.tableView endUpdates] provoque l' heightForRowAtIndexPath: pour les cellules visibles, et estimatedHeightForRowAtIndexPath: pour être appelé pour les cellules non visibles.

Les hauteurs de ligne variables ont un impact très négatif sur les performances de votre vue de table. Vous parlez du contenu Web affiché dans certaines cellules. Si nous ne parlons pas de milliers de lignes, envisager d' implémenter votre solution avec un UIWebView au lieu d'un UITableView pourrait être utile. Nous avons eu une situation similaire et sums allés avec un UIWebView avec un balisage HTML personnalisé et cela a fonctionné à merveille. Comme vous le savez probablement, vous avez un problème asynchronous désagréable lorsque vous avez une cellule dynamic avec un contenu Web:

  • Après avoir défini le contenu de la cellule, vous devez
  • attendez que la vue Web de la cellule ait terminé le rendu du contenu Web,
  • alors vous devez aller dans le UIWebView et – en utilisant JavaScript – requestr au document HTML à quelle hauteur il est
  • et PUIS mettre à jour la hauteur de UITableViewCell.

Pas de plaisir du tout et beaucoup de saut et de gigue pour l'user.

Si vous devez utiliser UITableView, mettez définitivement en cache les hauteurs de ligne calculées. De cette façon, il sera bon de les returnner dans heightForRowAtIndexPath: Au lieu de dire à UITableView quoi faire, il suffit de rendre votre source de données rapide.

Y a-t-il un moyen? La réponse est non.

Vous pouvez uniquement utiliser heightForRowAtIndexPath pour cela. Tout ce que vous pouvez faire est de rendre ceci aussi peu coûteux que possible en gardant par exemple un NSmutableArray de vos hauteurs de cellules dans votre model de données.

J'ai eu un problème similaire (saut saut de la tableview sur tout changement) parce que j'avais

  • (CGFloat) tableView: (UITableView *) tableView estimatedHeightForRowAtIndexPath: (NSIndexPath *) indexPath {return 500; }

commentant toute la fonction a aidé.

Utilisez la méthode UITableView suivante:

 - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation 

Vous devez spécifier un NSArray de NSIndexPath que vous voulez recharger. Si vous voulez recharger une seule cellule, vous pouvez fournir un NSArray qui ne contient qu'un seul NSIndexPath.

 NSIndexPath* rowTobeReloaded = [NSIndexPath indexPathForRow:1 inSection:0]; NSArray* rowsTobeReloaded = [NSArray arrayWithObjects:rowTobeReloaded, nil]; [UITableView reloadRowsAtIndexPaths:rowsTobeReloaded withRowAnimation:UITableViewRowAnimationNone]; 

La méthode heightForRowAtIndexPath: sera toujours appelée mais voici une solution de contournement que je suggérerais.

Chaque fois que l'user tape dans UITextView , enregistrez dans une variable locale l' indexPath de la cellule . Ensuite, lorsque heightForRowAtIndexPath: est appelé, vérifiez la valeur de l' indexPath enregistré. Si l' indexPath enregistré n'est pas nil , récupérez la cellule qui doit être redimensionnée et faites-le. Comme pour les autres cellules, utilisez vos valeurs mises en cache. Si l' indexPath enregistré est nil , exécutez vos lignes de code habituelles qui sont dans votre cas exigeantes.

Voici comment je reorderais de le faire:

Utilisez la tag de propriété de UITextView pour garder une trace de la ligne à resize.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ... [textView setDelegate:self]; [textView setTag:indexPath.row]; ... } 

Ensuite, dans la méthode textViewDidChange: votre délégué UITextView textViewDidChange: récupérez l' indexPath et stockez-le. savedIndexPath est une variable locale.

 - (void)textViewDidChange:(UITextView *)textView { savedIndexPath = [NSIndexPath indexPathForRow:textView.tag inSection:0]; } 

Enfin, vérifiez la valeur de savedIndexPath et exécutez ce dont vous savedIndexPath besoin.

 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if (savedIndexPath != nil) { if (savedIndexPath == indexPath.row) { savedIndexPath = nil; // return the new height } else { // return cached value } } else { // your normal calculating methods... } } 

J'espère que ça aide! Bonne chance.

J'ai fini par find un moyen de contourner le problème. J'ai été en mesure de pré-calculer la hauteur du contenu HTML que j'ai besoin de rendre, et d'inclure la hauteur ainsi que le contenu de la database. De cette façon, bien que je sois toujours obligé de fournir la hauteur pour toutes les cellules quand je mets à jour la hauteur de n'importe quelle cellule, je n'ai pas besoin de faire un rendu HTML coûteux, donc c'est assez accrocheur.

Malheureusement, cette solution ne fonctionne que si vous avez tout votre contenu HTML à l'avance.