Normalement, vous utiliseriez la méthode déléguée cellForRowAtIndexPath
pour configurer votre cellule. L’information définie pour la cellule est importante pour la manière dont la cellule est dessinée et sa taille.
Malheureusement, la méthode delegate heightForRowAtIndexPath
est appelée avant la méthode delegate cellForRowAtIndexPath
, nous ne pouvons donc pas simplement cellForRowAtIndexPath
au délégué de renvoyer la hauteur de la cellule, car elle sera nulle à ce moment-là.
Nous devons donc calculer la taille avant que la cellule ne soit dessinée dans le tableau. Heureusement, il existe une méthode qui ne fait que cela, sizeWithFont
, qui appartient à la classe NSSsortingng. Cependant, il y a un problème, afin de calculer la taille correcte dynamicment, il faut savoir comment les éléments dans la cellule seront présentés. Je vais le préciser dans un exemple:
Imaginez un UITableViewCell
, qui contient une étiquette nommée textLabel
. Dans la méthode déléguée cellForRowAtIndexPath
, nous textLabel.numberOfLines = 0
, ce qui indique à l’étiquette qu’elle peut comporter autant de lignes que nécessaire pour présenter le texte pour une largeur spécifique. Le problème se produit si nous atsortingbuons à textLabel un texte plus grand que la largeur donnée initialement à textLabel. La deuxième ligne apparaîtra, mais la hauteur de la cellule ne sera pas automatiquement ajustée et nous aurons donc une vue de la table à regarder.
Comme dit plus haut, nous pouvons utiliser sizeWithFont
pour calculer la hauteur, mais il faut savoir quelle police est utilisée, pour quelle largeur, etc. Si, pour des raisons de simplicité, nous nous intéressons uniquement à la largeur, nous pourrions coder en dur la largeur. autour de 320.0 (sans tenir compte du remplissage). Mais que se passerait-il si nous utilisions UITableViewStyleGrouped au lieu de plain, la largeur serait alors autour de 300.0 et la cellule serait à nouveau perturbée. Ou ce qui se passe si nous passons du portrait au paysage, nous avons beaucoup plus d’espace, mais il ne sera pas utilisé puisque nous avons codé en 300.0.
C’est le cas dans lequel, à un moment donné, vous devez vous demander à quel point vous pouvez éviter le codage en dur.
Vous pouvez appeler la méthode cellForRowAtIndexPath
appartenant à la classe UITableView pour obtenir la cellule d’une section et d’une ligne spécifiques. J’ai lu quelques articles qui disaient que tu ne voulais pas faire ça, mais je ne comprends pas vraiment ça. Oui, je suis d’accord pour dire que la cellule sera déjà allouée, mais la méthode delegate heightForRowAtIndexPath
est uniquement appelée pour les cellules qui seront visibles, de sorte que la cellule sera allouée de toute façon. Si vous utilisez correctement le dequeueReusableCellWithIdentifier
la cellule ne sera plus allouée dans la méthode cellForRowAtIndexPath
, à la place, un pointeur sera utilisé et les propriétés seront ajustées. Alors quel est le problème?
Notez que la cellule n’est PAS dessinée dans la méthode déléguée cellForRowAtIndexPath
, lorsque la cellule de vue de table devient visible, le script appelle la méthode setNeedDisplay
sur UITableVieCell qui déclenche la méthode drawRect
pour dessiner la cellule. Ainsi, appeler directement le délégué cellForRowAtIndexPath
ne perdra pas en performance car il doit être dessiné deux fois.
Bon, en appelant la méthode déléguée cellForRowAtIndexPath
dans la méthode delegate heightForRowAtIndexPath
, nous recevons toutes les informations nécessaires sur la cellule pour déterminer sa taille.
Peut-être pouvez-vous créer votre propre méthode sizeForCell
qui parcourt toutes les options, si la cellule se trouve dans le style Value1, ou Value2, etc.
C’est juste une théorie que j’ai décrite dans mes pensées, j’aimerais savoir si ce que j’ai écrit est correct. Ou que peut-être il y a une autre façon d’accomplir la même chose. Notez que je veux pouvoir faire des choses aussi flexibles que possible.
Oui, je suis d’accord pour dire que la cellule sera déjà allouée, mais la méthode delegate heightForRowAtIndexPath est uniquement appelée pour les cellules qui seront visibles, de sorte que la cellule sera allouée de toute façon.
Ceci est une erreur. La vue de table doit appeler heightForRowAtIndexPath
(si elle est implémentée) pour toutes les lignes heightForRowAtIndexPath
dans la vue table, et pas seulement celles actuellement affichées. La raison en est qu’il doit déterminer sa hauteur totale pour afficher les indicateurs de défilement corrects.
Je le faisais par:
Création d’une collection d’objects (tableau d’informations de taille (dictionnaire, NSNumber des hauteurs de lignes, etc.) en fonction des objects de la collection qui seront utilisés pour la vue de la table.
Cela se fait lorsque nous traitons les données depuis une source locale ou distante.
Je prédétermine le type et la taille de la police à utiliser lorsque je crée ces objects de collection. Vous pouvez même stocker les objects UIFont ou les objects personnalisés utilisés pour représenter le contenu.
Ces objects de collection seront utilisés chaque fois que j’implémenterai les protocoles UITableViewDataSource ou UITableViewDelegate pour déterminer la taille des instances UITableViewCell et de ses sous-vues, etc.
De cette façon, vous pouvez éviter de sous-classer UITableViewCell juste pour obtenir les différentes propriétés de taille de son contenu.
N’utilisez pas de valeur absolue pour initialiser les frameworks. Utilisez une valeur relative basée sur l’orientation et les limites actuelles.
Si vous le faites pivoter vers n’importe quelle orientation, faites simplement un mécanisme de redimensionnement à l’exécution. Assurez-vous que le paramètre autoresizingMask est défini correctement.
Vous n’avez besoin que des hauteurs, vous n’avez pas besoin de toutes ces choses inutiles dans un UITableViewCell pour déterminer la hauteur de la ligne. Vous n’avez peut-être même pas besoin de la largeur, car la valeur de la largeur doit être relative aux limites de la vue.
Voici mon approche pour résoudre ce problème
Comment mettre à jour la hauteur:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // We want the UIFont to be the same as what is in the nib, // but we dont want to call tableView dequeue a bunch because its slow. // If we make the font static and only load it once we can reuse it every // time we get into this method static UIFont* dynamicTextFont; static CGRect textFrame; static CGFloat extraHeight; if( !dynamicTextFont ) { DetailCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; dynamicTextFont = cell.resizeLabel.font; CGRect cellFrame = cell.frame; textFrame = cell.resizeLabel.frame; extraHeight = cellFrame.size.height-textFrame.size.height; // The space above and below the growing field } NSSsortingng* text = .... // Get this from the some object using indexPath CGSize size = [text sizeWithFont:dynamicTextFont constrainedToSize:CGSizeMake(textFrame.size.width, 200000.f) lineBreakMode:UILineBreakModeWordWrap]; return size.height+extraHeight; }
Problèmes:
Vous devriez jeter un oeil à TTTableItemCell.m
dans le framework Three20. Il suit une approche différente, essentiellement en faisant en sorte que chaque classe de cellule (avec des parameters prédéfinis comme la police, la disposition, etc.) implémente une méthode partagée + tableView: sizeForItem:
(ou quelque chose comme ça) . Lorsque vous recherchez le texte d’une cellule spécifique, vous pouvez également rechercher la police appropriée.
Concernant la hauteur de la cellule: Vous pouvez vérifier la largeur de votre tableView et, si nécessaire, soustraire les marges par UITableViewStyleGrouped
et la largeur de la barre d’index éventuelle et de l’élément de divulgation (que vous recherchez dans les données de vos cellules). Lorsque la largeur de la tableView change, par exemple par rotation de l’interface, vous devez appeler [tableView reloadData]
.
Pour répondre à la question posée, l’affiche originale demandait «est-ce que vous pouvez appeler cellForRowAtIndexPath?», Ce n’est pas le cas. Cela vous donnera une cellule mais elle ne l’allouera PAS à cet indexPath en interne et ne sera pas mise en queue (aucune méthode pour le remettre), vous allez donc la perdre. Je suppose que ce sera dans un pool d’autorelease et sera finalement libéré, mais vous créerez encore et encore des tas de cellules, ce qui est vraiment inutile.
Vous pouvez réaliser des hauteurs de cellule dynamics, vous pouvez même les rendre agréables, mais il est très difficile de les rendre parfaitement homogènes, encore plus si vous souhaitez prendre en charge plusieurs orientations, etc.
J’ai une idée sur la hauteur de cellule dynamic.
Créez simplement une instance de votre cellule personnalisée en tant que variable membre de UITableViewController
. Dans la tableView:heightForRowAtIndexPath:
méthode définit le contenu de la cellule et renvoie la hauteur de la cellule.
De cette façon, vous ne créerez pas / autoreleasing de cellules plusieurs fois comme vous le ferez si vous appelez cellForRowAtIndexPath
dans la méthode heightForRowAtIndexPath
.
UPD: Pour plus de commodité, vous pouvez également créer une méthode statique dans votre classe de cellules personnalisée qui créera une instance de cellule singleton pour le calcul de la hauteur, définira le contenu de la cellule et renverra sa hauteur.
tableView:heightForRowAtIndexPath:
corps de la fonction va maintenant ressembler à ceci:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return [MyCell cellHeightForContent:yourContent]; }
Voici ma solution que j’ai utilisée pour implémenter des cellules plutôt astucieuses pour une application de chat.
Jusqu’à ce point, j’ai toujours été très irrité par heightForCellAtIndexPath, car cela conduit à violer le principe DRY. Avec cette solution my heightForRowAtIndexPath: coûte 1,5 ms par cellule, ce qui pourrait me ramener à ~ 1 ms.
Fondamentalement, vous souhaitez que chaque sous-vue de votre cellule implémente sizeThatFits: Créez une cellule hors écran que vous configurez, puis interrogez la vue racine avec sizeThatFits: CGSizeMake (tableViewWidth, CGFLOAT_MAX).
Il y a quelques pièges en chemin. Certaines vues UIKit ont des opérations de réglage coûteuses. Par exemple, [UITextView setText] fait beaucoup de travail. L’astuce consiste à créer une sous-classe, à mettre la variable en mémoire tampon, puis à remplacer setNeedsDisplay pour appeler – [super setText:] lorsque la vue est sur le point d’être rendue. Bien entendu, vous devrez implémenter votre propre sizeThatFits: en utilisant les extensions UIKit.