Cómo determinar la altura de UICollectionView con FlowLayout

Tengo un UICollectionView con UICollectionViewFlowLayout , y quiero calcular su tamaño de contenido (para el retorno en intrinsicContentSize necesario para ajustar su altura mediante AutoLayout).

El problema es: incluso si tengo una altura fija e igual para todas las celdas, no sé cuántas “filas” / líneas tengo en UICollectionView . Tampoco puedo determinar el recuento por el número de elementos en mi fuente de datos, ya que las celdas que representan los elementos de datos varían en ancho, por lo tanto, también lo hace el número de elementos que tengo en una línea de UICollectionView .

Como no pude encontrar ninguna pista sobre este tema en la documentación oficial, y la búsqueda en Google no me llevó más lejos, cualquier ayuda e ideas serían muy apreciadas.

Whoa! Por alguna razón, después de horas de investigación, ahora encontré una respuesta bastante fácil a mi pregunta: estaba buscando completamente en el lugar equivocado, investigando toda la documentación que pude encontrar en UICollectionView .

La solución simple y fácil radica en el diseño subyacente: simplemente llame a collectionViewContentSize en su propiedad myCollectionView.collectionViewLayout y obtendrá el alto y el ancho del contenido como CGSize . Es tan fácil como eso.

En caso de que esté utilizando el diseño automático , podría crear una subclase de UICollectionView

Si usa debajo el código, entonces no tiene que especificar ninguna restricción de altura para la vista de colección, ya que variaría en función del contenido de la vista de colección.

A continuación se detalla la implementación:

 @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 

En viewDidAppear puede obtenerlo de la siguiente manera:

 float height = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height; 

Tal vez cuando vuelvas a cargar los datos, entonces necesitas calcular una nueva altura con los datos nuevos, luego puedes obtenerlos de la siguiente manera: agrega un observador para escuchar cuando tu CollectionView termine de volver a cargar los datos en viewdidload :

 [self.myCollectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL]; 

A continuación, agregue la función de abajo para obtener una nueva altura o haga algo después de que collectionview termine de recargar:

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { //Whatever you do here when the reloadData finished float newHeight = self.myCollectionView.collectionViewLayout.collectionViewContentSize.height; } 

Y no te olvides de eliminar al observador:

 [self.myCollectionView removeObserver:self forKeyPath:@"contentSize" context:NULL]; 

user1046037 respuesta en Swift …

 class DynamicCollectionView: UICollectionView { override func layoutSubviews() { super.layoutSubviews() if bounds.size != intrinsicContentSize() { invalidateIntrinsicContentSize() } } override func intrinsicContentSize() -> CGSize { return self.contentSize } } 

Código de Swift 3 para la respuesta del usuario1046037

 import UIKit class DynamicCollectionView: UICollectionView { override func layoutSubviews() { super.layoutSubviews() if !__CGSizeEqualToSize(bounds.size, self.intrinsicContentSize) { self.invalidateIntrinsicContentSize() } } override var intrinsicContentSize: CGSize { return contentSize } } 

Es muy tarde para responder esta pregunta, pero recientemente he tenido este problema.
La respuesta anterior de @ lee me ayuda mucho a obtener mi respuesta. Pero esa respuesta se limita al objective c y estaba trabajando rápido .
@lee Con referencia a su respuesta, permítame dejar que el usuario rápido solucione este problema fácilmente. Para eso, por favor siga los pasos a continuación:

Declarar una variable CGFloat en la sección de statement:

 var height : CGFloat! 

En viewDidAppear puede obtenerlo de la siguiente manera:

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

Tal vez cuando vuelvas a reload datos, entonces necesitas calcular una nueva altura con datos nuevos, luego puedes obtenerlos de la siguiente manera: addObserver para escuchar cuando tu CollectionView termine de volver a cargar los datos en viewWillAppear :

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

A continuación, agregue la función de abajo para obtener una nueva altura o haga algo después de que collectionview termine de recargar:

 override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { let newHeight : CGFloat = self.myCollectionView.collectionViewLayout.collectionViewContentSize().height var frame : CGRect! = self.myCollectionView.frame frame.size.height = newHeight self.myCollectionView.frame = frame } 

Y no te olvides de eliminar al observador:

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

Espero que esto te ayude a resolver tu problema rápidamente.

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 } } 

Swift versión 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 } }