Comment utilisez-vous la disposition automatique dans les UITableViewCell
s dans une vue de tableau pour laisser le contenu et les sous-vues de chaque cellule déterminer la hauteur de ligne (elle-même / automatiquement), tout en conservant des performances de défilement fluides?
ios
uitableview
autolayout
nsautolayout
row-height
smileyborg
la source
la source
contentSize
et dupreferredMaxLayoutWidth
travail. Voyez ici . Cela étant dit, si vous configurez correctement vos contraintes, vous ne devriez pas en avoir besoinpreferredMaxLayoutWidth
et, en fait, cela peut créer des résultats inattendus.Réponses:
TL; DR: Vous n'aimez pas lire? Accédez directement aux exemples de projets sur GitHub:
Description conceptuelle
Les 2 premières étapes ci-dessous sont applicables quelles que soient les versions d'iOS pour lesquelles vous développez.
1. Configurer et ajouter des contraintes
Dans votre
UITableViewCell
sous-classe, ajoutez des contraintes de sorte que les sous-vues de la cellule aient leurs bords épinglés aux bords de contentView de la cellule (surtout aux bords supérieur ET inférieur). REMARQUE: n'épinglez pas les sous-vues à la cellule elle-même; seulement aux cellulescontentView
! Laissez la taille de contenu intrinsèque de ces sous-vues déterminer la hauteur de la vue de contenu de la cellule de vue tableau en vous assurant que la résistance à la compression de contenu et les contraintes de contournement de contenu dans la dimension verticale de chaque sous-vue ne sont pas remplacées par les contraintes de priorité plus élevée que vous avez ajoutées. ( Hein? Cliquez ici. )N'oubliez pas que l'idée est d'avoir les sous-vues de la cellule connectées verticalement à la vue de contenu de la cellule afin qu'elles puissent «exercer une pression» et étendre la vue de contenu pour les adapter. En utilisant un exemple de cellule avec quelques sous-vues, voici une illustration visuelle de ce à quoi certaines (pas toutes!) De vos contraintes devraient ressembler:
Vous pouvez imaginer que plus de texte est ajouté à l'étiquette de corps multiligne dans l'exemple de cellule ci-dessus, il devra s'agrandir verticalement pour s'adapter au texte, ce qui forcera effectivement la cellule à augmenter en hauteur. (Bien sûr, vous devez obtenir les bonnes contraintes pour que cela fonctionne correctement!)
Obtenir vos bonnes contraintes est certainement la partie la plus difficile et la plus importante pour obtenir des hauteurs de cellule dynamiques avec Auto Layout. Si vous faites une erreur ici, cela pourrait empêcher tout le reste de fonctionner - alors prenez votre temps! Je recommande de configurer vos contraintes dans le code car vous savez exactement quelles contraintes sont ajoutées où, et il est beaucoup plus facile de déboguer en cas de problème. L'ajout de contraintes dans le code peut être aussi simple et nettement plus puissant que Interface Builder utilisant des ancres de mise en page ou l'une des fantastiques API open source disponibles sur GitHub.
updateConstraints
méthode de votre sous-classe UITableViewCell. Notez que celaupdateConstraints
peut être appelé plus d'une fois, donc pour éviter d'ajouter les mêmes contraintes plus d'une fois, assurez-vous d'encapsuler votre code d'ajout de contrainteupdateConstraints
dans une vérification pour une propriété booléenne telle quedidSetupConstraints
(que vous définissez sur YES après avoir exécuté votre contrainte -ajout de code une fois). En revanche, si vous disposez d'un code qui met à jour les contraintes existantes (telles que l'ajustement de laconstant
propriété sur certaines contraintes), placez-le dansupdateConstraints
mais en dehors de la vérification pourdidSetupConstraints
qu'il puisse s'exécuter à chaque appel de la méthode.2. Déterminer les identificateurs uniques de réutilisation des cellules de vue tabulaire
Pour chaque ensemble unique de contraintes dans la cellule, utilisez un identifiant de réutilisation de cellule unique. En d'autres termes, si vos cellules ont plus d'une disposition unique, chaque disposition unique doit recevoir son propre identifiant de réutilisation. (Un bon indice que vous devez utiliser un nouvel identifiant de réutilisation est lorsque votre variante de cellule a un nombre différent de sous-vues ou que les sous-vues sont organisées de manière distincte.)
Par exemple, si vous affichez un e-mail dans chaque cellule, vous pouvez avoir 4 mises en page uniques: messages avec uniquement un objet, messages avec un objet et un corps, messages avec un objet et une pièce jointe photo et messages avec un objet, corps et photo. Chaque disposition a des contraintes complètement différentes requises pour y parvenir, donc une fois que la cellule est initialisée et que les contraintes sont ajoutées pour l'un de ces types de cellules, la cellule doit obtenir un identifiant de réutilisation unique spécifique à ce type de cellule. Cela signifie que lorsque vous retirez une cellule de la réutilisation, les contraintes ont déjà été ajoutées et sont prêtes à être utilisées pour ce type de cellule.
Notez qu'en raison des différences de taille de contenu intrinsèque, les cellules avec les mêmes contraintes (type) peuvent toujours avoir des hauteurs différentes! Ne confondez pas des dispositions fondamentalement différentes (différentes contraintes) avec différents cadres de vue calculés (résolus à partir de contraintes identiques) en raison de différentes tailles de contenu.
Pour iOS 8 - Cellules à dimensionnement automatique
3. Activer l'estimation de la hauteur de ligne
Avec iOS 8, Apple a internalisé une grande partie du travail que vous deviez auparavant implémenter avant iOS 8. Pour permettre au mécanisme de cellule à dimensionnement automatique de fonctionner, vous devez d'abord définir la
rowHeight
propriété de la vue de table sur la constanteUITableViewAutomaticDimension
. Ensuite, vous devez simplement activer l'estimation de la hauteur de ligne en définissant laestimatedRowHeight
propriété de la vue de table sur une valeur différente de zéro, par exemple:Cela permet de fournir à la vue de table une estimation / espace réservé temporaire pour les hauteurs de ligne des cellules qui ne sont pas encore à l'écran. Ensuite, lorsque ces cellules sont sur le point de défiler à l'écran, la hauteur réelle de la ligne sera calculée. Pour déterminer la hauteur réelle de chaque ligne, la vue de tableau demande automatiquement à chaque cellule quelle hauteur elle
contentView
doit être basée sur la largeur fixe connue de la vue de contenu (qui est basée sur la largeur de la vue de table, moins tout élément supplémentaire comme un index de section ou vue accessoire) et les contraintes de disposition automatique que vous avez ajoutées à la vue de contenu et aux sous-vues de la cellule. Une fois cette hauteur de cellule réelle déterminée, l'ancienne hauteur estimée de la ligne est mise à jour avec la nouvelle hauteur réelle (et tous les ajustements apportés à contentSize / contentOffset de la vue tableau sont effectués selon vos besoins).D'une manière générale, l'estimation que vous fournissez n'a pas besoin d'être très précise - elle est uniquement utilisée pour dimensionner correctement l'indicateur de défilement dans la vue tableau, et la vue tableau fait un bon travail d'ajustement de l'indicateur de défilement pour les estimations incorrectes lorsque vous faites défiler les cellules à l'écran. Vous devez définir la
estimatedRowHeight
propriété sur la vue de table (dansviewDidLoad
ou similaire) sur une valeur constante qui est la hauteur de ligne "moyenne". Ce n'est que si la hauteur de vos lignes présente une variabilité extrême (par exemple, diffère d'un ordre de grandeur) et que vous remarquez l'indicateur de défilement "sauter" lorsque vous faites défiler si vous vous donnez la peine de mettretableView:estimatedHeightForRowAtIndexPath:
en œuvre le calcul minimal requis pour renvoyer une estimation plus précise pour chaque ligne.Pour la prise en charge iOS 7 (implémentation du dimensionnement automatique des cellules vous-même)
3. Faites une passe de mise en page et obtenez la hauteur de la cellule
Tout d'abord, instanciez une instance hors écran d'une cellule de vue de table, une instance pour chaque identifiant de réutilisation , qui est utilisée strictement pour les calculs de hauteur. (Hors écran, ce qui signifie que la référence de cellule est stockée dans une propriété / ivar sur le contrôleur de vue et n'est jamais retournée
tableView:cellForRowAtIndexPath:
pour que la vue de table s'affiche réellement à l'écran.) Ensuite, la cellule doit être configurée avec le contenu exact (par exemple, texte, images, etc.) qu'il tiendrait s'il devait être affiché dans la vue de table.Ensuite, la force de la cellule immédiatement présentation de ses sous - vues, puis utilisez la
systemLayoutSizeFittingSize:
méthode sur leUITableViewCell
« scontentView
pour savoir ce que la hauteur requise de la cellule est. UtilisezUILayoutFittingCompressedSize
pour obtenir la plus petite taille requise pour s'adapter à tout le contenu de la cellule. La hauteur peut ensuite être renvoyée par latableView:heightForRowAtIndexPath:
méthode déléguée.4. Utiliser les hauteurs de ligne estimées
Si votre vue de table contient plus de quelques dizaines de lignes, vous constaterez que l'exécution de la résolution automatique des contraintes de mise en page peut rapidement embourber le thread principal lors du premier chargement de la vue de table, comme cela
tableView:heightForRowAtIndexPath:
est appelé sur chaque ligne lors du premier chargement ( afin de calculer la taille de l'indicateur de défilement).Depuis iOS 7, vous pouvez (et devez absolument) utiliser la
estimatedRowHeight
propriété dans la vue de table. Cela permet de fournir à la vue de table une estimation / un espace réservé temporaire pour les hauteurs de ligne des cellules qui ne sont pas encore à l'écran. Ensuite, lorsque ces cellules sont sur le point de défiler à l'écran, la hauteur réelle de la ligne sera calculée (en appelanttableView:heightForRowAtIndexPath:
) et la hauteur estimée mise à jour avec celle réelle.D'une manière générale, l'estimation que vous fournissez n'a pas besoin d'être très précise - elle est uniquement utilisée pour dimensionner correctement l'indicateur de défilement dans la vue tableau, et la vue tableau fait un bon travail d'ajustement de l'indicateur de défilement pour les estimations incorrectes lorsque vous faites défiler les cellules à l'écran. Vous devez définir la
estimatedRowHeight
propriété sur la vue de table (dansviewDidLoad
ou similaire) sur une valeur constante qui est la hauteur de ligne "moyenne". Ce n'est que si la hauteur de vos lignes présente une variabilité extrême (par exemple, diffère d'un ordre de grandeur) et que vous remarquez l'indicateur de défilement "sauter" lorsque vous faites défiler si vous vous donnez la peine de mettretableView:estimatedHeightForRowAtIndexPath:
en œuvre le calcul minimal requis pour renvoyer une estimation plus précise pour chaque ligne.5. (Si nécessaire) Ajouter un cache de hauteur de ligne
Si vous avez fait tout ce qui précède et que vous trouvez toujours que les performances sont inacceptablement lentes lors de la résolution des contraintes
tableView:heightForRowAtIndexPath:
, vous devrez malheureusement implémenter une mise en cache pour les hauteurs de cellule. (C'est l'approche suggérée par les ingénieurs d'Apple.) L'idée générale est de laisser le moteur de mise en page automatique résoudre les contraintes la première fois, puis de mettre en cache la hauteur calculée pour cette cellule et d'utiliser la valeur mise en cache pour toutes les demandes futures pour la hauteur de cette cellule. L'astuce est bien sûr de vous assurer que vous effacez la hauteur mise en cache pour une cellule lorsque quelque chose se produit qui pourrait modifier la hauteur de la cellule - principalement, ce serait lorsque le contenu de cette cellule change ou lorsque d'autres événements importants se produisent (comme l'ajustement de l'utilisateur le curseur de taille de texte Dynamic Type).Exemple de code générique iOS 7 (avec beaucoup de commentaires juteux)
Exemples de projets
Ces projets sont des exemples pleinement opérationnels de vues de table avec des hauteurs de ligne variables en raison de cellules de vue de table contenant du contenu dynamique dans UILabels.
Xamarin (C # /. NET)
Si vous utilisez Xamarin, consultez cet exemple de projet mis en place par @KentBoogaart .
la source
heightForRowAtIndexPath
, en gardant la cellule et en la renvoyant la prochaine foiscellForRowAtIndexPath
.iOS8
mise en œuvre est-elle toujours recommandéeiOS9
etiOS10
, ou existe-t-il de nouvelles approches depuis la publication de cette réponse?Pour iOS 8 ci-dessus, c'est vraiment simple:
ou
Mais pour iOS 7, la clé est de calculer la hauteur après la mise en page automatique:
Important
Si plusieurs étiquettes de lignes, n'oubliez pas de définir le
numberOfLines
sur0
.N'oublie pas
label.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds)
L'exemple de code complet est ici .
la source
Exemple rapide d'un UITableViewCell à hauteur variable
Mis à jour pour Swift 3
La réponse Swift de William Hu est bonne, mais elle m'aide à avoir des étapes simples mais détaillées lorsque j'apprends à faire quelque chose pour la première fois. L'exemple ci-dessous est mon projet de test tout en apprenant à faire un
UITableView
avec des hauteurs de cellule variables. Je l'ai basé sur cet exemple de base UITableView pour Swift .Le projet terminé devrait ressembler à ceci:
Créer un nouveau projet
Il peut s'agir simplement d'une application à vue unique.
Ajoutez le code
Ajoutez un nouveau fichier Swift à votre projet. Nommez-le MyCustomCell. Cette classe contiendra les sorties pour les vues que vous ajoutez à votre cellule dans le storyboard. Dans cet exemple de base, nous n'aurons qu'une seule étiquette dans chaque cellule.
Nous connecterons cette prise plus tard.
Ouvrez ViewController.swift et assurez-vous que vous disposez du contenu suivant:
Note importante:
Ce sont les deux lignes de code suivantes (ainsi que la disposition automatique) qui rendent possible la hauteur de cellule variable:
Configurer le storyboard
Ajoutez une vue tabulaire à votre contrôleur de vue et utilisez la disposition automatique pour l'épingler sur les quatre côtés. Faites ensuite glisser une cellule de vue de tableau sur la vue de tableau. Et sur la cellule Prototype, faites glisser une étiquette. Utilisez la disposition automatique pour épingler l'étiquette sur les quatre bords de la vue de contenu de la cellule de vue de tableau.
Note importante:
Autres paramètres IB
Nom et identificateur de classe personnalisés
Sélectionnez la cellule d'affichage de tableau et définissez la classe personnalisée sur
MyCustomCell
(le nom de la classe dans le fichier Swift que nous avons ajouté). Définissez également l'identifiant surcell
(la même chaîne que celle utiliséecellReuseIdentifier
dans le code ci-dessus).Zéro lignes pour l'étiquette
Définissez le nombre de lignes
0
dans votre étiquette. Cela signifie plusieurs lignes et permet à l'étiquette de se redimensionner en fonction de son contenu.Branchez les prises
tableView
variable dans leViewController
code.myCellLabel
variable de laMyCustomCell
classe.Fini
Vous devriez pouvoir exécuter votre projet maintenant et obtenir des cellules de hauteurs variables.
Remarques
Si vous n'épinglez pas les bords d'attaque et de fuite (gauche et droite), vous devrez peut-être également définir les libellés de
preferredMaxLayoutWidth
sorte qu'il sache à quel moment du retour à la ligne. Par exemple, si vous avez ajouté une contrainte Centrer horizontalement à l'étiquette dans le projet ci-dessus plutôt que d'épingler les bords d'attaque et de fuite, vous devez ajouter cette ligne à latableView:cellForRowAtIndexPath
méthode:Voir également
la source
preferredMaxLayoutWidth
contre celui de la cellulecontentSize
? De cette façon, si vous avez un accessoireView ou qu'il a été glissé pour modification, il serait toujours pris en considération?J'ai encapsulé la solution iOS7 de smileyborg dans une catégorie
J'ai décidé d'envelopper cette solution intelligente de @smileyborg dans une
UICollectionViewCell+AutoLayoutDynamicHeightCalculation
catégorie.La catégorie corrige également les problèmes décrits dans la réponse de @ wildmonkey (chargement d'une cellule à partir d'une pointe et
systemLayoutSizeFittingSize:
retourCGRectZero
)Il ne prend en compte aucune mise en cache mais convient à mes besoins en ce moment. N'hésitez pas à le copier, le coller et le pirater.
UICollectionViewCell + AutoLayoutDynamicHeightCalculation.h
UICollectionViewCell + AutoLayoutDynamicHeightCalculation.m
Exemple d'utilisation:
Heureusement, nous n'aurons pas à faire ce jazz dans iOS8, mais c'est pour le moment!
la source
[YourCell new]
et l'utiliser comme mannequin. Tant que le code de construction du code de contrainte est déclenché dans votre instance et que vous déclenchez une passe de mise en page par programme, vous devriez être prêt à partir.UICollectionViews
également.Voici ma solution:
Vous devez dire
TableView
l'estimatedHeight
avant de charger la vue. Sinon, il ne pourra pas se comporter comme prévu.Objectif c
Mise à jour vers Swift 4.2
la source
estimatedRowHeight
varie ligne par ligne? dois-je surestimer ou sous-estimer? utiliser le minimum ou le maximum de la hauteur dans laquelle j'utilisetableView
?estimatedRowHeight
, cela affecte principalement la taille de la barre de défilement, jamais la hauteur des cellules réelles. Soyez audacieux dans la hauteur que vous choisissez: cela affectera l'animation d'insertion / suppression.La solution proposée par @smileyborg est presque parfaite. Si vous avez une cellule personnalisée et que vous en voulez une ou plusieurs
UILabel
avec des hauteurs dynamiques, la méthode systemLayoutSizeFittingSize combinée avec AutoLayout activé renvoie unCGSizeZero
sauf si vous déplacez toutes vos contraintes de cellule de la cellule vers son contentView (comme suggéré par @TomSwift ici Comment redimensionner la vue d'ensemble pour adapter toutes les sous-vues avec la mise en page automatique? ).Pour ce faire, vous devez insérer le code suivant dans votre implémentation UITableViewCell personnalisée (grâce à @Adrian).
Mélanger la réponse @smileyborg avec cela devrait fonctionner.
la source
systemLayoutSizeFittingSize
doit être appelé sur contentView, pas sur cellUn problème assez important que je viens de rencontrer pour répondre.
La réponse de @ smileyborg est généralement correcte. Cependant, si vous avez du code dans la
layoutSubviews
méthode de votre classe de cellule personnalisée, par exemple en définissant lepreferredMaxLayoutWidth
, il ne sera pas exécuté avec ce code:Cela m'a dérouté pendant un certain temps. Ensuite, je me suis rendu compte que c'est parce que ceux-ci ne déclenchent que layoutSubviews sur le
contentView
, pas la cellule elle-même.Mon code de travail ressemble à ceci:
Notez que si vous créez une nouvelle cellule, je suis sûr que vous n'avez pas besoin d'appeler
setNeedsLayout
car elle devrait déjà être définie. Dans les cas où vous enregistrez une référence à une cellule, vous devriez probablement l'appeler. Quoi qu'il en soit, cela ne devrait rien faire de mal.Une autre astuce si vous utilisez des sous-classes de cellules où vous définissez des choses comme
preferredMaxLayoutWidth
. Comme le mentionne @smileyborg, "votre cellule de vue de table n'a pas encore fixé sa largeur à la largeur de la vue de table". Cela est vrai et pose problème si vous effectuez votre travail dans votre sous-classe et non dans le contrôleur de vue. Cependant, vous pouvez simplement définir le cadre de cellule à ce stade en utilisant la largeur du tableau:Par exemple, dans le calcul de la hauteur:
(Il se trouve que je mets en cache cette cellule particulière pour la réutiliser, mais ce n'est pas pertinent).
la source
Au cas où les gens auraient toujours des problèmes avec ça. J'ai écrit un petit article de blog sur l'utilisation de la mise en page automatique avec UITableViews tirant parti de la mise en page automatique pour Dynamic Cell Heights ainsi qu'un composant open source pour aider à rendre cela plus abstrait et plus facile à implémenter. https://github.com/Raizlabs/RZCellSizeManager
la source
Tant que votre disposition dans votre cellule est bonne.
Mise à jour: vous devez utiliser le redimensionnement dynamique introduit dans iOS 8.
la source
tableView:cellForRowAtIndexPath:
en cetableView:heightForRowAtIndexPath:
moment?systemLayoutSizeFittingSize:
entableView:cellForRowAtIndexPath:
cache le résultat, puis quetableView:heightForRowAtIndexPath:
je l' utilise, cela fonctionne bien tant que les contraintes sont correctement configurées, bien sûr!(pour Xcode 8.x / Xcode 9.x lu en bas)
Méfiez-vous du problème suivant dans Xcode 7.x, qui pourrait être une source de confusion:
Interface Builder ne gère pas correctement la configuration automatique des cellules. Même si vos contraintes sont absolument valables, IB se plaindra toujours et vous donnera des suggestions et des erreurs déroutantes. La raison en est qu'IB n'est pas disposé à modifier la hauteur de la ligne selon vos contraintes (afin que la cellule s'adapte autour de votre contenu). Au lieu de cela, il maintient la hauteur de la ligne fixe et commence à vous suggérer de modifier vos contraintes, que vous devez ignorer .
Par exemple, imaginez que vous avez tout configuré correctement, aucun avertissement, aucune erreur, tout fonctionne.
Maintenant, si vous changez la taille de la police (dans cet exemple, je change la taille de la police de l'étiquette de description de 17,0 à 18,0).
Étant donné que la taille de la police a augmenté, l'étiquette souhaite désormais occuper 3 lignes (avant, elle occupait 2 lignes).
Si Interface Builder fonctionnait comme prévu, il redimensionnerait la hauteur de la cellule pour s'adapter à la nouvelle hauteur d'étiquette. Cependant, ce qui se passe réellement, c'est qu'IB affiche l'icône d'erreur de mise en page automatique rouge et vous suggère de modifier les priorités de compression / étreinte.
Vous devez ignorer ces avertissements. Ce que vous pouvez * faire à la place est de modifier manuellement la hauteur de la ligne dans (sélectionnez Cellule> Inspecteur de taille> Hauteur de ligne).
Je modifiais cette hauteur un clic à la fois (en utilisant le stepper haut / bas) jusqu'à ce que les erreurs de la flèche rouge disparaissent! (vous obtiendrez en fait des avertissements jaunes, à ce moment, allez-y et faites des `` mises à jour des cadres '', tout devrait fonctionner).
* Notez que vous n'avez pas réellement à résoudre ces erreurs rouges ou jaunes dans Interface Builder - au moment de l'exécution, tout fonctionnera correctement (même si IB affiche des erreurs / avertissements). Assurez-vous simplement qu'au moment de l'exécution dans le journal de la console, vous n'obtenez aucune erreur de mise en page automatique.
En fait, essayer de toujours mettre à jour la hauteur de ligne dans IB est super ennuyeux et parfois presque impossible (en raison de valeurs fractionnaires).
Pour éviter les avertissements / erreurs gênants de l'IB, vous pouvez sélectionner les vues concernées et,
Size Inspector
pour la propriété,Ambiguity
choisirVerify Position Only
Xcode 8.x / Xcode 9.x semble (parfois) faire les choses différemment de Xcode 7.x, mais toujours de manière incorrecte. Par exemple, même lorsque
compression resistance priority
/hugging priority
est défini sur requis (1000), Interface Builder peut étirer ou découper une étiquette pour l'adapter à la cellule (au lieu de redimensionner la hauteur de la cellule pour qu'elle s'adapte autour de l'étiquette). Et dans un tel cas, il pourrait même ne pas afficher d'alerte ou d'erreur AutoLayout. Ou parfois, il fait exactement ce que Xcode 7.x a fait, décrit ci-dessus.la source
Pour définir la dimension automatique pour la hauteur de ligne et la hauteur de ligne estimée, assurez-vous que les étapes suivantes sont effectives, la dimension automatique est efficace pour la disposition de hauteur de cellule / ligne.
UITableViewAutomaticDimension
à rowHeight et estimeRowHeightheightForRowAt
et lui renvoyer une valeurUITableViewAutomaticDimension
)-
Objectif c:
Rapide:
Pour l'instance d'étiquette dans UITableviewCell
Remarque : Si vous avez plusieurs étiquettes (UIElements) avec une longueur dynamique, qui doit être ajustée en fonction de la taille de son contenu: ajustez la `` Priorité à l'étirement du contenu et à la résistance à la compression '' pour les étiquettes que vous souhaitez développer / compresser avec une priorité plus élevée.
la source
tableView: heightForRow
source de données.tableView: heightForRow
. (pour iOS 10-tableView: heightForRow
est requis)tableView: heightForRow
..if (indexPath.row == 0) { return 100} else { return UITableView.automaticDimension }
Comme @ Bob-Spryn, j'ai rencontré un problème assez important pour que je poste cela comme une réponse.
J'ai lutté pendant un moment avec la réponse de @ smileyborg . La chasse aux sorcières que je suis tombé est si vous avez défini votre cellule prototype en IB avec des éléments supplémentaires (
UILabels
,UIButtons
, etc.) dans IB lorsque vous instancier la cellule avec [[YourTableViewCellClass alloc] init]
il ne sera pas instancier tous les autres éléments à l' intérieur de cette cellule , sauf si vous avez un code écrit pour le faire. (J'ai eu une expérience similaire avecinitWithStyle
.)Pour que le storyboard instancie tous les éléments supplémentaires, obtenez votre cellule avec
[tableView dequeueReusableCellWithIdentifier:@"DoseNeeded"]
(Pas[tableView dequeueReusableCellWithIdentifier:forIndexPath:]
car cela causera des problèmes intéressants.) Lorsque vous faites cela, tous les éléments que vous avez définis dans IB seront instanciés.la source
Hauteur dynamique des cellules de la vue tabulaire et disposition automatique
Un bon moyen de résoudre le problème avec la mise en page automatique du storyboard:
la source
la source
Une autre «solution»: évitez toute cette frustration et utilisez plutôt UIScrollView pour obtenir un résultat qui ressemble et se sent identique à UITableView.
Ce fut la douloureuse "solution" pour moi, après avoir mis au total plus de 20 heures très frustrantes à essayer de construire quelque chose comme ce que smileyborg a suggéré et échoué pendant plusieurs mois et trois versions des versions de l'App Store.
Mon avis est que si vous avez vraiment besoin du support iOS 7 (pour nous, c'est essentiel), la technologie est tout simplement trop fragile et vous vous arracherez les cheveux en essayant. Et que UITableView est complètement exagéré en général à moins que vous n'utilisiez certaines des fonctionnalités avancées d'édition de lignes et / ou que vous n'ayez vraiment besoin de prendre en charge plus de 1000 "lignes" (dans notre application, il n'y a en réalité jamais plus de 20 lignes).
Le bonus supplémentaire est que le code devient incroyablement simple par rapport à toute la merde de délégué et de va-et-vient qui vient avec UITableView. Il s'agit d'une seule boucle de code dans viewOnLoad qui a l'air élégante et facile à gérer.
Voici quelques conseils pour le faire:
À l'aide de Storyboard ou d'un fichier nib, créez un ViewController et la vue racine associée.
Faites glisser un UIScrollView sur votre vue racine.
Ajoutez des contraintes en haut, en bas, à gauche et à droite à la vue de niveau supérieur afin que UIScrollView remplisse la vue racine entière.
Ajoutez une UIView à l'intérieur de l'UIScrollView et appelez-la "conteneur". Ajoutez des contraintes supérieure, inférieure, gauche et droite à UIScrollView (son parent). ASTUCE CLÉ: Ajoutez également des contraintes de «largeurs égales» pour lier UIScrollView et UIView.
REMARQUE: vous obtiendrez une erreur «la vue de défilement a une hauteur de contenu défilable ambiguë» et votre UIView de conteneur doit avoir une hauteur de 0 pixel. Aucune de ces erreurs ne semble avoir d'importance lorsque l'application est en cours d'exécution.
Créez des fichiers nib et des contrôleurs pour chacune de vos "cellules". Utilisez UIView et non UITableViewCell.
Dans votre ViewController racine, vous ajoutez essentiellement toutes les «lignes» à l'UIView du conteneur et ajoutez par programme des contraintes liant leurs bords gauche et droit à la vue du conteneur, leurs bords supérieurs soit à la vue du conteneur (pour le premier élément), soit à la précédente. cellule. Reliez ensuite la dernière cellule au fond du conteneur.
Pour nous, chaque "ligne" est dans un fichier nib. Le code ressemble donc à ceci:
Et voici le code pour UITools.addViewToTop:
Le seul "piège" que j'ai trouvé avec cette approche jusqu'à présent est que UITableView a une belle fonctionnalité d'en-têtes de section "flottants" en haut de la vue lorsque vous faites défiler. La solution ci-dessus ne le fera que si vous ajoutez plus de programmation, mais pour notre cas particulier, cette fonctionnalité n'était pas 100% essentielle et personne n'a remarqué quand elle est partie.
Si vous voulez des séparateurs entre vos cellules, ajoutez simplement une UIView de 1 pixel de haut au bas de votre "cellule" personnalisée qui ressemble à un séparateur.
Assurez-vous d'activer "rebonds" et "rebondir verticalement" pour que le contrôle de rafraîchissement fonctionne et il ressemble donc plus à une table.
TableView affiche des lignes et des séparateurs vides sous votre contenu, s'il ne remplit pas le plein écran, contrairement à cette solution. Mais personnellement, je préfère que ces lignes vides ne soient pas là de toute façon - avec une hauteur de cellule variable, il m'a toujours semblé "buggé" de toute façon d'y avoir les lignes vides.
En espérant qu'un autre programmeur lit mon message AVANT de perdre plus de 20 heures à essayer de le comprendre avec Table View dans leur propre application. :)
la source
J'ai dû utiliser des vues dynamiques (configurer les vues et les contraintes par code) et quand j'ai voulu définir la largeur de l'étiquette favoriteMaxLayoutWidth était 0. J'ai donc une mauvaise hauteur de cellule.
J'ai ensuite ajouté
avant d'exécuter
Après cette largeur d'étiquette était comme prévu et la hauteur dynamique calculait à droite.
la source
Supposons que vous ayez une cellule avec une sous-vue et que vous souhaitiez que la hauteur de la cellule soit suffisamment élevée pour englober la sous-vue + le remplissage.
1) Définissez la contrainte inférieure de la sous-vue égale à cell.contentView moins le remplissage souhaité. Ne définissez pas de contraintes sur la cellule ou cell.contentView elle-même.
2) Sélectionnez l' un de l'tableView
rowHeight
propriété outableView:heightForRowAtIndexPath:
àUITableViewAutomaticDimension
.3) Définissez la
estimatedRowHeight
propriété de tableView outableView:estimatedHeightForRowAtIndexPath:
une meilleure estimation de la hauteur.C'est ça.
la source
Si vous faites la mise en page par programme, voici ce qu'il faut considérer pour iOS 10 en utilisant des ancres dans Swift.
Il y a trois règles / étapes
NUMÉRO 1: définissez ces deux propriétés de tableview sur viewDidLoad, la première indique à la tableview qui devrait s'attendre à des tailles dynamiques sur leurs cellules, la seconde consiste simplement à laisser l'application calculer la taille de l'indicateur de la barre de défilement, ce qui aide à performance.
NUMÉRO 2: Il est important que vous ayez besoin d'ajouter les sous-vues au contentView de la cellule et non à la vue, et d'utiliser également ses présentationsmarginguide pour ancrer les sous-vues en haut et en bas, ceci est un exemple pratique de la façon de le faire.
Créez une méthode qui ajoutera les sous-vues et effectuerez la mise en page, appelez-la dans la méthode init.
NUMÉRO 3: NE PAS APPELER LA MÉTHODE:
Si vous le faites, vous remplacerez votre implémentation.
Suivez ces 3 règles pour les cellules dynamiques dans les vues de table.
voici une implémentation fonctionnelle https://github.com/jamesrochabrun/MinimalViewController
la source
Si vous avez une longue chaîne. par exemple, qui n'a pas de saut de ligne. Ensuite, vous pourriez rencontrer des problèmes.
Le correctif «présumé» est mentionné par la réponse acceptée et quelques autres réponses. Vous avez juste besoin d'ajouter
Je trouve que la réponse de Suragh est la plus complète et la plus concise , donc sans confusion.
Bien que non expliquer pourquoi ces changements sont nécessaires. Faisons cela.
Déposez le code suivant dans un projet.
Notez que je n'ai ajouté aucune contrainte de taille . Je n'ai ajouté que des contraintes centerX, centerY. Mais l'étiquette sera toujours dimensionnée correctement Pourquoi?
À cause de
contentSize
.Pour mieux traiter cela, gardez d'abord l'étape 0, puis commentez les étapes 1 à 6. Restons
setupLayout()
. Observez le comportement.Décommentez ensuite l'étape 1 et observez.
Décommentez ensuite l'étape 2 et observez.
Faites-le jusqu'à ce que vous ayez décommenté les 6 étapes et observé leurs comportements.
Que peut-on conclure de tout cela? Quels facteurs peuvent changer le
contenSize
?\n
la largeur de intrinsicContentSize sera la largeur maximale de toutes les lignes. Si une ligne a 25 caractères, une autre a 2 caractères et une autre a 21 caractères alors votre largeur sera calculée sur la base des 25 caractèresnumberOfLines
sur0
sinon vous n'aurez pas plusieurs lignes. VotrenumberOfLines
ajustera votre intrinsicContentSize hauteurFaire des ajustements: imaginez qu'en fonction de votre texte, la largeur
200
et la hauteur de votre intrinsicContentSize étaient100
, mais vous vouliez limiter la largeur au conteneur de l'étiquette, qu'allez-vous faire? La solution consiste à le régler à la largeur souhaitée. Pour ce faire, définissezpreferredMaxLayoutWidth
sur130
alors votre nouveau intrinsicContentSize aura une largeur d'environ130
. La hauteur serait évidemment plus que100
parce que vous auriez besoin de plus de lignes. Cela étant dit, si vos contraintes sont correctement définies, vous n'aurez pas besoin de l'utiliser du tout! Pour en savoir plus, voir cette réponse et ses commentaires. Vous ne devez l'utiliser quepreferredMaxLayoutWidth
si vous n'avez pas de contraintes limitant la largeur / hauteur comme dans l'un pourrait dire "ne pas envelopper le texte à moins qu'il dépasse lapreferredMaxLayoutWidth
». Mais avec 100% de certitude si vous définissez le premier / arrière etnumberOfLines
à0
alors vous êtes bon! Longue histoire courte la plupart des réponses ici qui recommande de l' utiliser sont MAUVAIS! Vous n'avez pas besoin. Il est un ayant besoin de signe que vos contraintes ne sont pas définis correctement ou que vous n'avez tout simplement pas de contraintesTaille de police: Notez également que si vous augmentez votre fontSize, la hauteur de intrinsicContentSize augmentera. Je ne l'ai pas montré dans mon code. Vous pouvez l'essayer par vous-même.
Revenons donc à votre exemple tableViewCell:
Il vous suffit de:
numberOfLines
à0
preferredMaxLayoutWidth
.la source
Dans mon cas, je dois créer une cellule personnalisée avec une image provenant du serveur et pouvant avoir n'importe quelle largeur et hauteur. Et deux UILabels avec taille dynamique (largeur et hauteur)
j'ai obtenu la même chose ici dans ma réponse avec la mise en page automatique et par programme:
Fondamentalement, la réponse @smileyBorg a aidé, mais systemLayoutSizeFittingSize n'a jamais fonctionné pour moi, dans mon approche:
1. Aucune utilisation de la propriété de calcul automatique de la hauteur de ligne. 2. Pas d'utilisation de la hauteur estimée 3. Pas besoin de mise à jour inutile Contraintes. 4.Aucune utilisation de la largeur de présentation maximale préférée automatique. 5. Aucune utilisation de systemLayoutSizeFittingSize (devrait avoir une utilité mais ne fonctionne pas pour moi, je ne sais pas ce qu'il fait en interne), mais à la place ma méthode - (float) getViewHeight fonctionne et je sais ce qu'elle fait en interne.
Est-il possible d'avoir des hauteurs différentes dans une cellule UITableView lorsque j'utilise plusieurs façons différentes d'afficher la cellule?
la source
Dans mon cas, le remplissage était dû aux hauteurs sectionHeader et sectionFooter, où le storyboard m'a permis de le changer au minimum 1. Donc, dans la méthode viewDidLoad:
la source
Je viens de faire une stupide tentative d'erreur avec les 2 valeurs de
rowHeight
et j'ai justeestimatedRowHeight
pensé que cela pourrait fournir des informations de débogage:Si vous les définissez tous les deux OU seulement définissez le,
estimatedRowHeight
vous obtiendrez le comportement souhaité:Nous vous suggérons de faire de votre mieux pour obtenir une estimation correcte, mais le résultat final n'est pas différent. Cela affectera simplement vos performances.
Si vous définissez uniquement rowHeight, c'est-à-dire uniquement:
votre résultat final ne serait pas comme souhaité:
Si vous définissez le
estimatedRowHeight
sur 1 ou plus petit, vous vous planterez indépendamment durowHeight
.Je suis tombé en panne avec le message d'erreur suivant:
la source
En ce qui concerne la réponse acceptée par @smileyborg, j'ai trouvé
ne pas être fiable dans certains cas où les contraintes sont ambiguës. Mieux vaut forcer le moteur de disposition à calculer la hauteur dans une direction, en utilisant la catégorie d'assistance sur UIView ci-dessous:
Où w: est la largeur de la table
la source
Ajoutez simplement ces deux fonctions dans votre viewcontroller, cela résoudra votre problème. Ici, la liste est un tableau de chaînes qui contient votre chaîne de chaque ligne.
la source
la source
Si la hauteur de la cellule est dynamique par le contenu, vous devez la compter précisément, puis renvoyer la valeur de la hauteur avant le rendu de la cellule. Un moyen simple consiste à définir la méthode de comptage dans le code de cellule de vue de table pour que le contrôleur appelle à la méthode de délégué de hauteur de cellule de table. N'oubliez pas de compter la largeur réelle du cadre de cellule (320 par défaut) si la hauteur dépend de la largeur du tableau ou de l'écran. Autrement dit, dans la méthode déléguée de hauteur de cellule de tableau, utilisez cell.frame pour corriger d'abord la largeur de cellule, puis appelez la méthode de hauteur de comptage définie dans la cellule pour obtenir la valeur appropriée et la renvoyer. .
PS. Le code pour générer l'objet de cellule pourrait être défini dans une autre méthode pour que la méthode de délégué de cellule de vue de table différente puisse appeler.
la source
UITableView.automaticDimension
peut être défini via Interface Builder:Xcode> Storyboard> Inspecteur de taille
Cellule de vue Tableau> Hauteur de ligne> Automatique
la source
encore une autre solution iOs7 + iOs8 dans Swift
la source
cellForRowAtIndexPath
, la cellule n'est pas encore disposée à ce stade.