J'essaie de faire UICollectionViewCells
fonctionner le dimensionnement automatique avec la mise en page automatique, mais je n'arrive pas à faire en sorte que les cellules s'adaptent au contenu. J'ai du mal à comprendre comment la taille de la cellule est mise à jour à partir du contenu de ce qui se trouve dans le contenu de la cellule.
Voici la configuration que j'ai essayée:
- Personnalisé
UICollectionViewCell
avec unUITextView
dans son contentView. - Le défilement pour
UITextView
est désactivé. - La contrainte horizontale de contentView est: "H: | [_textView (320)]", c'est-à-dire qu'elle
UITextView
est épinglée à gauche de la cellule avec une largeur explicite de 320. - La contrainte verticale de contentView est: "V: | -0 - [_ textView]", c'est-à-dire
UITextView
épinglé en haut de la cellule. - Le
UITextView
a une contrainte de hauteur définie sur une constante à laquelle lesUITextView
rapports s'adapteront au texte.
Voici à quoi cela ressemble avec l'arrière-plan de la cellule défini sur rouge et l' UITextView
arrière - plan défini sur bleu:
J'ai mis le projet avec lequel je jouais sur GitHub ici .
Réponses:
Mis à jour pour Swift 5
preferredLayoutAttributesFittingAttributes
renommépreferredLayoutAttributesFitting
et utiliser le dimensionnement automatiqueMis à jour pour Swift 4
systemLayoutSizeFittingSize
renommé ensystemLayoutSizeFitting
Mis à jour pour iOS 9
Après avoir vu ma solution GitHub casser sous iOS 9, j'ai finalement eu le temps d'examiner le problème de manière approfondie. J'ai maintenant mis à jour le référentiel pour inclure plusieurs exemples de configurations différentes pour les cellules à dimensionnement automatique. Ma conclusion est que les cellules à dimensionnement automatique sont excellentes en théorie mais en désordre dans la pratique. Un mot de prudence lors de l'utilisation de cellules à dimensionnement automatique.
TL; DR
Découvrez mon projet GitHub
Les cellules à dimensionnement automatique ne sont prises en charge qu'avec la disposition de flux, assurez-vous donc que c'est ce que vous utilisez.
Il y a deux choses que vous devez configurer pour que les cellules à dimensionnement automatique fonctionnent.
1. Ensemble
estimatedItemSize
surUICollectionViewFlowLayout
La disposition du flux deviendra de nature dynamique une fois que vous aurez défini la
estimatedItemSize
propriété.2. Ajoutez un support pour le dimensionnement de votre sous-classe de cellules
Cela vient en 2 saveurs; Disposition automatique ou remplacement personnalisé de
preferredLayoutAttributesFittingAttributes
.Créer et configurer des cellules avec la disposition automatique
Je n'entrerai pas dans les détails à ce sujet car il y a un article SO brillant sur la configuration des contraintes pour une cellule. Méfiez-vous simplement que Xcode 6 a cassé un tas de choses avec iOS 7 donc, si vous prenez en charge iOS 7, vous devrez faire des choses comme s'assurer que le masque de redimensionnement automatique est défini sur contentView de la cellule et que les limites de contentView sont définies comme limites de la cellule lorsque la cellule est chargée (ie
awakeFromNib
).Ce que vous devez savoir, c'est que votre cellule doit être plus sérieusement contrainte qu'une cellule de vue Table. Par exemple, si vous voulez que votre largeur soit dynamique, votre cellule a besoin d'une contrainte de hauteur. De même, si vous voulez que la hauteur soit dynamique, vous aurez besoin d'une contrainte de largeur pour votre cellule.
Implémentez
preferredLayoutAttributesFittingAttributes
dans votre cellule personnaliséeLorsque cette fonction est appelée, votre vue a déjà été configurée avec du contenu (c'est-à-dire qu'elle
cellForItem
a été appelée). En supposant que vos contraintes ont été correctement définies, vous pouvez avoir une implémentation comme celle-ci:REMARQUE Sur iOS 9, le comportement a changé un peu, ce qui pourrait entraîner des plantages sur votre implémentation si vous ne faites pas attention (voir plus ici ). Lorsque vous implémentez,
preferredLayoutAttributesFittingAttributes
vous devez vous assurer que vous ne modifiez le cadre de vos attributs de disposition qu'une seule fois. Si vous ne le faites pas, la mise en page appellera votre implémentation indéfiniment et finira par planter. Une solution consiste à mettre en cache la taille calculée dans votre cellule et à l'invalider chaque fois que vous réutilisez la cellule ou modifiez son contenu comme je l'ai fait avec laisHeightCalculated
propriété.Découvrez votre mise en page
À ce stade, vous devriez avoir des cellules dynamiques «fonctionnelles» dans votre collectionView. Je n'ai pas encore trouvé la solution prête à l'emploi suffisante lors de mes tests alors n'hésitez pas à commenter si vous en avez. Il se sent toujours comme
UITableView
gagne la bataille pour le dimensionnement dynamique à mon humble avis.Avertissements
N'oubliez pas que si vous utilisez des cellules prototypes pour calculer la taille estimée de l'élément, cela se cassera si votre XIB utilise des classes de taille . La raison en est que lorsque vous chargez votre cellule à partir d'un XIB, sa classe de taille sera configurée avec
Undefined
. Cela ne sera rompu que sur iOS 8 et versions ultérieures, car sur iOS 7, la classe de taille sera chargée en fonction de l'appareil (iPad = Regular-Any, iPhone = Compact-Any). Vous pouvez soit définir l'estimationItemSize sans charger le XIB, soit charger la cellule à partir du XIB, l'ajouter à la collectionView (cela définira le traitCollection), effectuer la mise en page, puis la supprimer de la vue d'ensemble. Alternativement, vous pouvez également faire en sorte que votre cellule remplace letraitCollection
getter et retourne les traits appropriés. C'est à vous.Faites-moi savoir si j'ai raté quelque chose, j'espère avoir aidé et bonne chance le codage
la source
estimatedItemSize
entraîne des plantages - il semble y avoir un énorme bogue dans la façon dont la mise en page automatique essaie de traiter l'UICollectionViewCell.Dans iOS10, il y a une nouvelle constante appelée
UICollectionViewFlowLayout.automaticSize
(anciennementUICollectionViewFlowLayoutAutomaticSize
), donc à la place:vous pouvez utiliser ceci:
Il a de meilleures performances, en particulier lorsque les cellules de votre vue de collection ont une largeur constante
Accès à la disposition des flux:
Swift 5 mis à jour:
la source
collectionViewLayout
et vérifier simplement qu'elle est de typeUICollectionViewFlowLayout
Quelques changements clés dans la réponse de Daniel Galasko ont résolu tous mes problèmes. Malheureusement, je n'ai pas assez de réputation pour commenter directement (encore).
À l'étape 1, lors de l'utilisation de la mise en page automatique, ajoutez simplement un UIView parent unique à la cellule. TOUT à l'intérieur de la cellule doit être une sous-vue du parent. Cela a répondu à tous mes problèmes. Bien que Xcode ajoute automatiquement cela pour UITableViewCells, il ne le fait pas (mais il devrait) pour UICollectionViewCells. Selon les documents :
Ignorez ensuite l'étape 3 entièrement. Ce n'est pas nécessaire.
la source
contentView
, Xcode se plaint qu'elle entre en conflit avec une propriété existante. J'ai essayé d'ajouter des sous-vuesself.contentView
et de définir des contraintes, puis l'application se bloque.Dans iOS 10+, il s'agit d'un processus très simple en 2 étapes.
Assurez-vous que tout le contenu de vos cellules est placé dans une seule UIView (ou dans un descendant d'UIView comme UIStackView qui simplifie beaucoup la mise en page automatique). Tout comme avec le redimensionnement dynamique des UITableViewCells, la hiérarchie de la vue entière doit avoir des contraintes configurées, du conteneur le plus externe à la vue la plus interne. Cela inclut les contraintes entre UICollectionViewCell et la vue enfant immédiate
Demander à la structure de flux de votre UICollectionView de dimensionner automatiquement
la source
UICollectionViewFlowLayout.automaticSize
a été renomméUICollectionViewFlowLayoutAutomaticSize
.Ajouter flowLayout sur viewDidLoad ()
Définissez également un UIView comme conteneur principal pour votre cellule et ajoutez toutes les vues requises à l'intérieur.
Reportez-vous à ce tutoriel impressionnant et époustouflant pour plus de référence: UICollectionView avec cellule de redimensionnement automatique à l'aide de la mise en page automatique dans iOS 9 et 10
la source
EDIT 19/11/19: Pour iOS 13, utilisez simplement UICollectionViewCompositionalLayout avec des hauteurs estimées. Ne perdez pas votre temps à gérer cette API cassée.
Après avoir lutté avec cela pendant un certain temps, j'ai remarqué que le redimensionnement ne fonctionne pas pour UITextViews si vous ne désactivez pas le défilement:
la source
contentView ancre mystère:
Dans un cas bizarre, cela
ne fonctionnerait pas. Ajout de quatre ancres explicites à contentView et cela a fonctionné.
et comme d'habitude
dans
YourLayout: UICollectionViewFlowLayout
Qui sait? Pourrait aider quelqu'un.
Crédit
https://www.vadimbulavin.com/collection-view-cells-self-sizing/
trébuché sur la pointe là-bas - je ne l'ai jamais vu nulle part ailleurs dans tous les articles des 1000 sur ce sujet.
la source
J'ai fait une hauteur de cellule dynamique de la vue de la collection. Voici le dépôt git hub .
Et, découvrez pourquoi preferLayoutAttributesFittingAttributes est appelé plusieurs fois. En fait, il sera appelé au moins 3 fois.
L'image du journal de la console:
1er PreferredLayoutAttributesFittingAttributes :
Le layoutAttributes.frame.size.height est le statut actuel 57.5 .
2ème PreferredLayoutAttributesFittingAttributes :
La hauteur du cadre cellulaire est passée à 534,5 comme prévu. Mais, la vue de la collection reste à hauteur nulle.
3e PreferredLayoutAttributesFittingAttributes :
Vous pouvez voir que la hauteur de la vue de la collection est passée de 0 à 477 .
Le comportement est similaire pour gérer le défilement:
Au début, je pensais que cette méthode n'appelait qu'une seule fois. J'ai donc codé comme suit:
Cette ligne:
entraînera la boucle infinie des appels système et le blocage de l'application.
Toute taille modifiée, il validera encore et encore toutes les cellules préférées de LayoutAttributesFittingAttributes jusqu'à ce que les positions de chaque cellule (c'est-à-dire les cadres) ne changent plus.
la source
En plus des réponses ci-dessus,
Il suffit de vous rendre vous définissez estimatedItemSize propriété de UICollectionViewFlowLayout une certaine taille et ne pas mettre en œuvre sizeForItem: atIndexPath méthode déléguée.
C'est tout.
la source
Si vous implémentez la méthode UICollectionViewDelegateFlowLayout:
- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath*)indexPath
Lorsque vous appelez
collectionview performBatchUpdates:completion:
, la taille de taille utiliserasizeForItemAtIndexPath
au lieu depreferredLayoutAttributesFittingAttributes
.Le processus de rendu
performBatchUpdates:completion
passera par la méthodepreferredLayoutAttributesFittingAttributes
mais il ignore vos modifications.la source
À qui cela peut aider,
J'ai eu ce vilain crash si
estimatedItemSize
c'était réglé. Même si je suis retourné 0 ponumberOfItemsInSection
. Par conséquent, les cellules elles-mêmes et leur mise en page automatique n'étaient pas à l'origine du crash ... La collectionView vient de planter, même lorsqu'elle est vide, simplement parce qu'elle aestimatedItemSize
été définie pour l'auto-dimensionnement.Dans mon cas, j'ai réorganisé mon projet, d'un contrôleur contenant une collectionView à un collectionViewController, et cela a fonctionné.
Allez comprendre.
la source
collectionView.collectionViewLayout.invalidateLayout()
aprèscollectionView.reloadData()
.Pour tous ceux qui ont tout essayé sans chance, c'est la seule chose qui l'a fait fonctionner pour moi. Pour les étiquettes multilignes à l'intérieur de la cellule, essayez d'ajouter cette ligne magique:
Plus d'infos: ici
À votre santé!
la source
L'exemple de méthode ci-dessus ne se compile pas. Voici une version corrigée (mais non testée pour savoir si cela fonctionne ou non.)
la source
Mettre à jour plus d'informations:
Si vous utilisez
flowLayout.estimatedItemSize
, suggérez d'utiliser la version ultérieure d'iOS8.3. Avant iOS8.3, il plantera[super layoutAttributesForElementsInRect:rect];
. Le message d'erreur est*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Deuxièmement, dans la version iOS8.x, un
flowLayout.estimatedItemSize
paramètre d'encart de section différent ne fonctionnera pas. fonction -à- dire:(UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:
.la source
La solution comprend 4 étapes importantes:
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
contentView
sur les bords de la cellule car ellecontentView
empêche le dimensionnement automatique, étant donné que la disposition automatique n'est pas activée.collectionView(:cellForItemAt:)
pour limiter la largeur de contentView à la largeur de collectionView.Cela se passe comme suit; nous allons définir la
maxWidth
variable de la cellule sur la largeur de CollectionView selon la méthode decollectionView(:cellForItemAt:)
et dansmaxWidth
,didSet
nous allons définir la largeurAnchor.constant sur maxWidth.Puisque vous souhaitez activer l'auto-dimensionnement de UITextView, il a une étape supplémentaire pour;
4. Calculez et définissez la hauteurAnchor.constant de UITextView.
Ainsi, chaque fois que la largeur de contentView est définie, nous ajustons la hauteur de UITextView le long
didSet
demaxWidth
.Dans UICollectionViewCell:
Ces étapes vous permettront d'obtenir le résultat souhaité. Voici un runnable complet essentiel
Merci à Vadim Bulavin pour son article de blog clair et instructif - Collection View Cells Self-Sizing: Step by Step Tutorial
la source
J'ai essayé d'utiliser,
estimatedItemSize
mais il y avait un tas de bugs lors de l'insertion et de la suppression de cellules si ellesestimatedItemSize
n'étaient pas exactement égales à la hauteur de la cellule. J'ai arrêté de définirestimatedItemSize
et mis en œuvre des cellules dynamiques en utilisant une cellule prototype. voici comment cela se fait:créer ce protocole:
implémentez ce protocole à votre façon
UICollectionViewCell
:maintenant, faites en sorte que votre contrôleur se conforme à
UICollectionViewDelegateFlowLayout
ce champ:il sera utilisé comme cellule prototype pour lier des données à, puis déterminer comment ces données ont affecté la dimension que vous souhaitez dynamiser
enfin, le
UICollectionViewDelegateFlowLayout's
collectionView(:layout:sizeForItemAt:)
doit être implémenté:et c'est tout. Renvoyer la taille de la cellule de
collectionView(:layout:sizeForItemAt:)
cette manière m'empêche d'avoir à utiliserestimatedItemSize
, et l'insertion et la suppression de cellules fonctionne parfaitement.la source