web-dev-qa-db-fra.com

UICollectionView taille automatique

Comment redimensionner correctement UICollectionView afin qu'il affiche complètement son contenu? J'ai essayé beaucoup de choses, y compris définir son cadre, appeler reloadData et invalider la mise en page:

self.collectionView.contentSize = CGSizeMake(300, 2000);
self.collectionView.frame = CGRectMake(0, 0, 300, 2000);
[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];

mais rien de tout cela n'a aucun effet. Après avoir appuyé sur le bouton, je vois toujours la vue initiale, comme ceci:

collection view only showing part of the content after frame resize

J'ai un petit programme de démonstration où j'ai une source de données produisant 100 éléments. Dans Interface Builder, j'ai initialement défini la taille de UICollectionView sur une petite valeur, de sorte que tous les éléments ne tiennent pas, puis j'appuie sur un bouton après lequel le code ci-dessus est exécuté. Je pense qu'UICollectionView affiche maintenant tous les éléments, mais ce n'est pas le cas.

EDIT: Le programme de démonstration est disponible sur https://github.com/mjdemilliano/TestUICollectionView .

EDIT2: J'ai observé que la mise à jour de l'image était perdue à un moment donné, car si j'appuyais de nouveau sur le bouton, l'image actuelle retrouvait l'ancienne valeur. Après l'ajout de quelques instructions de journal dans le gestionnaire d'événements du bouton , la sortie du journal est:

before: frame = {{0, 58}, {320, 331}}, contentSize = {320, 1190}
update button pressed
after: frame = {{0, 0}, {300, 2000}}, contentSize = {300, 2000}
before: frame = {{0, 58}, {320, 331}}, contentSize = {320, 1190}
update button pressed
after: frame = {{0, 0}, {300, 2000}}, contentSize = {300, 2000}

_ {Je ne comprends pas pourquoi le changement de cadre n'est pas conservé, qu'est-ce qui le change?}

À un moment donné, je remplacerai les valeurs codées en dur par des valeurs obtenues à partir de la structure de flux, mais je voulais l'exclure et garder mon exemple aussi simple que possible.

Contexte: Ce que je veux faire par la suite est le suivant: J'ai une vue avec défilement avec divers contrôles comme les étiquettes et les images, et une vue de collection avec un contenu dynamique. Je veux faire défiler tout cela, pas seulement la vue de collection, donc je n'utilise pas les fonctions de défilement de la vue de collection, qui fonctionnent bien.

47
Martijn de Milliano

J'ai finalement résolu ce problème en résolvant tous les problèmes de mise en page automatique, la hauteur de la vue de collection à l'aide d'une contrainte. Ensuite, chaque fois que je sais que le contenu a changé, je mets à jour la valeur de la contrainte en utilisant la valeur collectionView.contentSize.height:

self.verticalLayoutConstraint.constant = self.collectionView.contentSize.height;

La vue de collection est ensuite correctement redimensionnée et se comporte bien dans la vue de défilement globale. J'ai mis à jour le projet de test GitHub avec mes modifications.

Pour moi, faire cela en mettant à jour la contrainte manuellement au lieu de pouvoir dire à iOS: "rendre la hauteur de trame de la vue de collection aussi grande que nécessaire" ne me semble pas juste, mais c'est le meilleur que j'ai jamais trouvé jusque là. S'il vous plaît envoyer une meilleure réponse si vous en avez un.

102
Martijn de Milliano

Voici ma mise en œuvre dans Swift 3:

override func sizeThatFits(_ size: CGSize) -> CGSize {
    if (self.superview != nil) {
        self.superview?.layoutIfNeeded()
    }

    return collectionView.contentSize
}
3
Craig Holliday
UICollectionViewFlowLayout *flowLayout;
flowLayout = [[UICollectionViewFlowLayout alloc]init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:0.0f];
[self.collectionView setPagingEnabled:NO];
[flowLayout setItemSize:CGSizeMake(322.0, 148.0)];  //important to leave no white space between the images
[self.collectionView setCollectionViewLayout:flowLayout];

J'ai trouvé que l'autolayout dans le storyboard n'aide pas beaucoup. Un paramètre correct pour UICollectionViewFlowLayout pour votre collectionView est la véritable aide. Si vous ajustez la taille de l'élément avec setItemSize, vous obtiendrez peut-être le résultat souhaité.

2
flame3

Voici un moyen de lier la hauteur de CollectionView via sa taille intrinsèque ..__ Je l'ai utilisée pour dimensionner correctement un CollectionView dans une cellule TableView (avec une hauteur de cellules dynamiques). et cela fonctionne parfaitement.

Tout d’abord, ajoutez ceci à votre sous-classe UICollectionView:

override var intrinsicContentSize: CGSize {
    get {
        return self.contentSize
    }
}

Ensuite, appelez layoutIfNeeded () après avoir rechargé des données:

reloadData()
layoutIfNeeded()
2
AmitP

La méthode la plus simple que j'ai trouvée consiste à remplacer les méthodes sizeThatFits: telles quelles:

- (CGSize)sizeThatFits:(CGSize)size
{
    if( self.superview )
        [self.superview layoutIfNeeded]; // to force evaluate the real layout

    return self.collectionViewLayout.collectionViewContentSize;
}
1
Nicolas Buquet

Cela semble bien fonctionner avec une classe UICollectionView personnalisée. 

class AutoSizedCollectionView: UICollectionView {

    override var contentSize: CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

Définissez votre classe personnalisée dans le générateur d'interface:

 enter image description here

De cette manière, vous pouvez également définir la taille intrinsèque de vos vues de collection sur 'espace réservé' dans le générateur d'interface pour éviter de définir une contrainte de hauteur.

 enter image description here

J'espère que ça aidera quelqu'un d'autre.

0
Trevor

Vous pouvez essayer ma classe AGCollectionView personnalisée

  • Affectez une contrainte de hauteur de collectionView à l'aide d'un storyboard ou par programme.

- Attribuez cette classe à votre UICollectionView.

class AGCollectionView: UICollectionView {

    fileprivate var heightConstraint: NSLayoutConstraint!

    override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) {
        super.init(frame: frame, collectionViewLayout: layout)
        self.associateConstraints()
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.associateConstraints()
    }

    override open func layoutSubviews() {
        super.layoutSubviews()

        if self.heightConstraint != nil {
            self.heightConstraint.constant = floor(self.contentSize.height)
        }
        else{
            self.sizeToFit()
            print("Set a heightConstraint set size to fit content")
        }
    }

    func associateConstraints() {
        // iterate through height constraints and identify

        for constraint: NSLayoutConstraint in constraints {
            if constraint.firstAttribute == .height {
                if constraint.relation == .equal {
                    heightConstraint = constraint
                }
            }
        }
    }
}
0
AshvinGudaliya