42 votes

Iphone - quand calculer heightForRowAtIndexPath pour une tableview lorsque chaque hauteur de cellule est dynamique?

J'ai vu cette question posée plusieurs fois, mais étonnamment, je n'ai pas vu de réponse uniforme, donc je vais l'essayer moi-même:

Si vous avez un tableview contenant vos propres UITableViewCells qui contiennent UITextViews et UILabels dont la hauteur doit être déterminé au moment de l'exécution, comment êtes-vous censé pour déterminer la hauteur de chaque ligne dans heightForRowAtIndexPath?

La plus évidente de la première idée est de calculer la hauteur de chaque cellule par le calcul, puis en additionnant les hauteurs de chaque point de vue à l'intérieur de la cellule à l'intérieur de cellForRowAtIndexPath, et magasin de finale de hauteur totale pour une récupération ultérieure.

Cela ne fonctionnera pas cependant parce que cellForRowAtIndexPath est appelée APRÈS heightForRowAtIndexPath.

La seule chose que je peux penser à est de faire tous les calculs à l'intérieur de viewDidLoad, de créer tous les UITableViewCells ensuite, calculer les cellules de la hauteur et de la stocker dans un champ personnalisé à l'intérieur de votre UITableViewCell sous-classe, et de mettre chaque cellule dans un NSMutableDictionary avec le indexPath comme la clé, puis il vous suffit de récupérer de la cellule du dictionnaire à l'aide de la indexPath à l'intérieur de cellForRowAtIndexPath et heightForRowAtIndexPath, renvoyant soit à la hauteur personnalisée de la valeur ou de la cellule de l'objet lui-même.

Cette approche semble erroné car il ne fait pas usage de dequeueReusableCellWithIdentifier, au lieu de cela, je serais le chargement de toutes les cellules à la fois dans un dictionnaire dans mon contrôleur, et le délégué des méthodes serait de ne rien faire de plus que de récupérer la bonne cellule à partir du dictionnaire.

Je ne vois pas d'autre façon de le faire bien. Est-ce une mauvaise idée - si oui, quelle est la bonne façon de le faire?

62voto

Obliquely Points 4465

La manière d'Apple implémente UITableView n'est pas intuitif pour tout le monde et il est facile de mal comprendre le rôle de l' heightForRowAtIndexPath:. L'intention générale est que c'est plus rapide et léger-sur-méthode de la mémoire qui peut être appelée pour chaque ligne de la table assez fréquemment. Cela contraste avec cellForRowAtIndexPath: qui est souvent plus lent et plus gourmande en mémoire, mais est appelée uniquement pour les lignes qui sont réellement besoin d'être affichés à tout moment.

Pourquoi Apple en œuvre de ce genre? Partie de la raison est qu'il est presque toujours moins cher (ou peut être moins cher si vous le code de droit) pour calculer la hauteur d'une ligne qu'il ne l'est de construire et de remplir une cellule entière. Étant donné que, dans de nombreux tableaux de la hauteur de chaque cellule sera identique, il est souvent beaucoup moins coûteuse. Et une autre partie de la raison est parce que iOS a besoin de savoir la taille de l'ensemble de la table: cela lui permet de créer des barres de défilement et de l'installer sur un parchemin vue etc.

Donc, à moins que toutes les cellules de la hauteur est la même, quand un UITableView est créé et chaque fois que vous envoyez un reloadData message, la source de données est envoyé un heightForRowAtIndexPath message pour chaque cellule. Donc, si votre table a 30 cellules, ce message est envoyé à 30 fois. Disons seulement six de ces 30 cellules sont visibles sur l'écran. Dans ce cas, lors de leur création et lorsque vous l'envoyez un reloadData message, la UITableView enverra un cellForRowAtIndexPath message par ligne visible, c'est à dire que le message est envoyé à six reprises.

Certaines personnes sont parfois perplexe sur la façon de calculer une hauteur de cellule sans créer les vues elles-mêmes. Mais généralement, c'est facile à faire.

Par exemple, si votre ligne de hauteurs varient en taille, car ils contiennent des quantités variables de texte, vous pouvez utiliser l'une de l' sizeWithFont: méthodes sur la chaîne appropriée pour effectuer les calculs. C'est plus rapide que la construction d'un point de vue et puis de mesurer le résultat. Notez que si vous modifiez la hauteur d'une cellule, vous devez recharger l'ensemble de la table (avec reloadData - ce qui va demander au délégué pour chaque hauteur, mais seulement demander des cellules visibles) OU de manière sélective recharger les lignes où la taille a changé (ce qui, la dernière fois que j'ai vérifié, les appels heightForRowAtIndexPath: sur la ligne , mais aussi fait un peu de défilement pour faire bonne mesure).

Voir cette question et peut-être aussi cette une.

10voto

lxt Points 22990

Donc, je pense que vous pouvez le faire sans avoir à créer vos cellules tout à la fois (ce qui, comme vous le suggérez, c'est du gaspillage et probablement aussi peu pratique pour un grand nombre de cellules).

UIKit ajoute un couple de méthodes pour NSString, vous avez manquée, comme ils ne font pas partie des principaux NSString de la documentation. Ceux de l'intérêt pour vous de commencer:

- (CGSize)sizeWithFont...

Voici le lien vers l'Apple docs.

En théorie, ces NSString ajouts existent pour ce problème précis: pour déterminer la taille d'un bloc de texte qui prendra place sans avoir besoin de charger la vue elle-même. Vous avez sans doute déjà avoir accès au texte de chaque cellule dans le cadre de votre vue de la table de source de données.

Je dis "en théorie" parce que si vous faites la mise en forme de votre UITextView votre kilométrage peut varier avec cette solution. Mais j'espère que cela va vous arriver au moins à mi-chemin là. Il y a un exemple sur le Cacao est Ma petite Amie.

5voto

Une approche que j'ai utilisé dans le passé pour créer une variable de classe pour contenir une et une seule instance de la cellule que vous allez utiliser dans la table (je l'appelle un prototype de cellule). Puis dans la cellule personnalisé de classe, j'ai une méthode pour remplir les données et de déterminer la hauteur de la cellule doit être. Notez qu'elle peut être une simple variante de la méthode de vraiment remplir les données - au lieu de réellement le redimensionnement d'un UILabel dans une cellule, par exemple, il est possible d'utiliser le NSString hauteur des méthodes pour déterminer quelle est la taille de la UILabel serait dans la dernière cellule, puis d'utiliser la totalité des cellules de la hauteur (en plus d'une frontière sur le fond) et UILabel de placement afin de déterminer la vraie hauteur. Vous utilisez le prototype de la cellule juste pour avoir une idée de l'endroit où les éléments sont placés de sorte que vous savez ce que cela signifie quand un label va être de 44 unités de haut.

En heightForRow: je puis appeler cette méthode pour revenir à la hauteur.

En cellForRow: - je utiliser la méthode qui remplit les étiquettes et les redimensionne (vous n'avez jamais redimensionner la UITableView de cellules de même).

Si vous voulez obtenir la fantaisie, vous pouvez également mettre en cache la hauteur de chaque cellule sur la base des données que vous transmettez (par exemple, il pourrait juste être sur un NSString si c'est tout ce qui détermine la hauteur). Si vous avez beaucoup de données qui sont souvent les mêmes, il peut être judicieux d'avoir en permanence un cache au lieu de simplement en mémoire.

Vous pouvez également essayer d'estimer le nombre de lignes basé sur le personnage ou le nombre de mots, mais dans mon expérience, qui ne fonctionne jamais et quand ça ne va pas habituellement, il bousille une cellule et toutes les cellules en dessous.

5voto

WrightsCS Points 32170

C'est de cette façon-je calculer la hauteur d'une cellule basée sur la quantité de texte dans un UTextView:

#define PADDING  21.0f

- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    if(indexPath.section == 0 && indexPath.row == 0)
    {   
        NSString *practiceText = [practiceItem objectForKey:@"Practice"];
        CGSize practiceSize = [practiceText sizeWithFont:[UIFont systemFontOfSize:14.0f] 
                   constrainedToSize:CGSizeMake(tblPractice.frame.size.width - PADDING * 3, 1000.0f)];
        return practiceSize.height + PADDING * 3;
    }

    return 72;
}

Bien sûr, vous devez régler l' PADDING et d'autres variables pour s'adapter à vos besoins, mais il permet de définir la hauteur de la cellule qui a un UITextView dans, selon la quantité de texte fournie. donc si il y a seulement 3 lignes de texte, la cellule est assez court, où comme si il n'y a 14 lignes de texte, la cellule est plutôt de grande hauteur.

3voto

Malcolm Box Points 2427

La meilleure mise en œuvre de ce que j'ai vu est le moyen le Three20 TTTableView classes de le faire.

Essentiellement, ils ont une classe dérivée de UITableViewController que les délégués de l' heightForRowAtIndexPath: méthode à une méthode d'une classe sur une TTTableCell classe.

Cette catégorie renvoie ensuite à la bonne hauteur, toujours par faire le même genre de mise en page des calculs comme vous le faites dans le tirage des méthodes. En le déplaçant vers la classe, il évite d'écrire un code qui dépend de la cellule de l'instance.

Il n'y a vraiment pas d'autre option - pour des raisons de performances, le cadre ne va pas créer des cellules avant de demander de leurs hauteurs, et vous ne voulez pas vraiment à le faire si il pourrait y avoir beaucoup de lignes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X