web-dev-qa-db-fra.com

Comment déterminer la hauteur de UICollectionView avec FlowLayout

J'ai un UICollectionView avec un UICollectionViewFlowLayout et je veux calculer la taille de son contenu (pour le retour dans intrinsicContentSize nécessaire pour ajuster sa hauteur via AutoLayout). 

Le problème est le suivant: même si j'ai une hauteur fixe et égale pour toutes les cellules, je ne sais pas combien de "lignes"/lignes il y a dans le UICollectionView. Je ne peux pas non plus déterminer ce nombre en fonction du nombre d'éléments de ma source de données, car les cellules qui représentent les éléments de données varient en largeur. Par conséquent, le nombre d'éléments que j'ai dans une ligne de UICollectionView.

Étant donné que je ne trouvais aucune indication à ce sujet dans la documentation officielle et que la recherche sur Google ne m'apportait rien de plus, toute aide ou idée serait très appréciée.

101
Ignatius Tremor

Whoa! Pour une raison quelconque, après des heures de recherche, j’ai trouvé une réponse assez facile à ma question: j’étais en train de chercher au mauvais endroit, en fouillant dans toute la documentation que j’ai pu trouver sur UICollectionView.

La solution simple et facile réside dans la disposition sous-jacente: Il suffit d'appeler collectionViewContentSize sur votre propriété myCollectionView.collectionViewLayout et vous obtenez la hauteur et la largeur du contenu sous la forme CGSize. C'est aussi simple que ça.

233
Ignatius Tremor

Si vous utilisez Mise en page automatique , vous pouvez créer une sous-classe de UICollectionView

Si vous utilisez le code ci-dessous, vous n’avez pas à spécifier de contrainte de hauteur pour la vue Collection, car elle varie en fonction du contenu de la vue Collection. 

Ci-dessous est la mise en œuvre:

@interface DynamicCollectionView : UICollectionView

@end

@implementation DynamicCollectionView

- (void) layoutSubviews
{
    [super layoutSubviews];

    if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize]))
    {
        [self invalidateIntrinsicContentSize];
    }
}

- (CGSize)intrinsicContentSize
{
    CGSize intrinsicContentSize = self.contentSize;

    return intrinsicContentSize;
}

@end
33
user1046037

user1046037 répondre en Swift ...

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize() {
            invalidateIntrinsicContentSize()
        }
    }

    override func intrinsicContentSize() -> CGSize {
        return self.contentSize
    }
}
11
Nick Wood

Swift 3 code pour l'utilisateur1046037 answer

import UIKit

class DynamicCollectionView: UICollectionView {

    override func layoutSubviews() {
        super.layoutSubviews()
        if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
            self.invalidateIntrinsicContentSize()
        }

    }

    override var intrinsicContentSize: CGSize {
        return contentSize
    }

}
8

Il est trop tard pour répondre à cette question, mais j'ai récemment parcouru ce problème.
@ lee's ci-dessus réponse aidez-moi beaucoup à obtenir ma réponse. Mais cette réponse est limitée à objective-c et je travaillais à Swift.
@ lee En ce qui concerne votre réponse, permettez-moi de laisser l'utilisateur Swift résoudre ce problème facilement. Pour cela, veuillez suivre les étapes ci-dessous:

Déclarez une variable CGFloat dans la section de déclaration:

var height : CGFloat!

À viewDidAppear vous pouvez l'obtenir par:

height = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

Peut-être que lorsque vos données reload devront alors calculer une nouvelle hauteur avec de nouvelles données, vous pourrez les obtenir par: addObserver pour écouter lorsque votre CollectionView aura fini de recharger les données à viewWillAppear:

override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(true)
        ....
        ....
        self.shapeCollectionView.addObserver(self, forKeyPath: "contentSize", options: NSKeyValueObservingOptions.Old, context: nil)
    }

Ajoutez ensuite la fonction ci-dessous pour obtenir une nouvelle hauteur ou effectuez n'importe quoi après que collectionview ait terminé le rechargement:

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        let newHeight : CGFloat = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height

        var frame : CGRect! = self.myCollectionView.frame
        frame.size.height = newHeight

        self.myCollectionView.frame = frame
    }

Et n'oubliez pas d'enlever l'observateur:

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(true)
    ....
    ....
    self.myCollectionView.removeObserver(self, forKeyPath: "contentSize")
}

J'espère que cela vous aidera à résoudre votre problème dans Swift.

2
Er. Vihar

Swift 4

class DynamicCollectionView: UICollectionView {
  override func layoutSubviews() {
    super.layoutSubviews()
    if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) {
      self.invalidateIntrinsicContentSize()
    }
  }

  override var intrinsicContentSize: CGSize {
    return collectionViewLayout.collectionViewContentSize
  }
}
2
Wilson

Swift 4.2

class DynamicCollectionView: UICollectionView {
    override func layoutSubviews() {
        super.layoutSubviews()
        if bounds.size != intrinsicContentSize {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        return self.contentSize
    }
}
1
Cesare

Swift 4.2, Xcode 10.1, iOS 12.1:

Pour une raison quelconque, collectionView.contentSize.height apparaissait plus petit que la hauteur résolue de ma vue de collection. Premièrement, j'utilisais une contrainte de mise en page automatique relative à la moitié de la hauteur de l'aperçu. Pour résoudre ce problème, j'ai modifié la contrainte afin qu'elle soit relative à la "zone de sécurité" de la vue.

Cela m'a permis de définir la hauteur de la cellule pour remplir ma vue de collection à l'aide de collectionView.contentSize.height:

private func setCellSize() {
    let height: CGFloat = (collectionView.contentSize.height) / CGFloat(numberOfRows)
    let width: CGFloat = view.frame.width - CGFloat(horizontalCellMargin * 2)

    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: width, height: height)
}

Avant

 height constraint relative to superview  bad simulator result 

Après

 height constraint relative to safe area  good simulator result 

0
Andrew Kirna

Version rapide de @ user1046037:

public class DynamicCollectionView: UICollectionView {

    override public func layoutSubviews() {
        super.layoutSubviews()
        if !bounds.size.equalTo(intrinsicContentSize) {
            invalidateIntrinsicContentSize()
        }
    }

    override public var intrinsicContentSize: CGSize {
        let intrinsicContentSize: CGSize = contentSize
        return intrinsicContentSize
    }
}
0
Joshua Hart

De plus: vous feriez mieux d’appeler reloadData avant d’obtenir la hauteur: myCollectionView.collectionViewLayout.collectionViewContentSize

0
guozqzzu