ignaciovarela / uicollectionviewcell-dynamic-size

0 stars 0 forks source link

Existe un overhead importante al calcular los tamaños de las celdas #1

Open fedetrim opened 8 years ago

fedetrim commented 8 years ago

Si la collection va a mostrar 1000 celdas, te estás creando 1000 templateCell al principio de todo, lo cual hace muy pesada la carga inicial de la pantalla. (Si lo probás con 1000 celdas vas a ver qué tarda algunos segundos en cargar el controller y mostrarlo)

Creo que la solución sería settear esto:

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)

Y creo que una vez setteado eso, no es necesario overridear sizeForItem (no está chequeado, hay que probarlo)

ignaciovarela commented 8 years ago

En realidad no está creando 1000 templateCell al principio de todo. Crea una sola pero después por cada ítem se llama a sizeForItemAtIndexPath y se calcula el tamaño de la celda configurada con el ítem correspondiente (ahí aplica las constraints). Esto genera un mínimo overhead al principio pero después no se vuelve a hacer el cálculo del tamaño.

¿Por qué decís que no es necesario hacer el override de sizeForItemAtIndexPath? ¿Cómo sabe el flowLayout que tamaño debería tener la celda?

fedetrim commented 8 years ago
    func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {

        guard let item = items?[indexPath.item],
            templateCell = templateCell,
            flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
            else { return CGSizeZero }

        templateCell.configure(item.text)

        let numberOfColumns = UIApplication.sharedApplication().statusBarOrientation.isPortrait ? Constants.numberOfColumnsPortrait : Constants.numberOfColumnsLandscape
        let sumOfSpaceBetweenCells = flowLayout.minimumInteritemSpacing * (numberOfColumns - 1)
        let availableWidth = collectionView.frame.size.width - flowLayout.sectionInset.left - flowLayout.sectionInset.right - sumOfSpaceBetweenCells
        let cellWidth = floor(availableWidth / numberOfColumns)

        return templateCell.computeCellSizeWith(width: cellWidth)
    }

Este método se llama una vez por cada ítem que tenga tu collection (fijate que te pide el size para determinado NSIndexPath. Si tenés 1000 posibles NSIndexPath, entonces se llama 1000 veces). Por lo tanto estás creando 1000 celdas, y por cada una, ejecutando lógica pesada para calcular tamaños. Además, estas llamadas son consecutivas y se hacen por única vez, al cargar la collection (a menos que recargues algún ítem, por supuesto).

La siguiente línea...

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)

...le dice al layout de la collection que el contenido de tus celdas es dinámico, y en vez de pedirte el tamaño de cada una explícitamente en el collectionView( _, layout: _, sizeForItemAtIndexPath: _), directamente se lo pide al sistema de Auto Layout al tener que dibujar un ítem en particular.

ignaciovarela commented 8 years ago

Si usamos eso, tenemos que implementar en la celda el método:

    override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {

        layoutAttributes.bounds.size.height = systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
        return layoutAttributes
    }

Y después habría que ver como hacer para que en el estimatedItemSize pasarle el width correcto. Porque la idea era tener un layout a una columna en portrait y otro layout a dos columnas en landscape. Y esto lo lograba en sizeForItemAtIndexPath haciendo:

        let numberOfColumns = UIApplication.sharedApplication().statusBarOrientation.isPortrait ? Constants.numberOfColumnsPortrait : Constants.numberOfColumnsLandscape
        let sumOfSpaceBetweenCells = flowLayout.minimumInteritemSpacing * (numberOfColumns - 1)
        let availableWidth = collectionView.frame.size.width - flowLayout.sectionInset.left - flowLayout.sectionInset.right - sumOfSpaceBetweenCells
        let cellWidth = floor(availableWidth / numberOfColumns)
fedetrim commented 8 years ago

No no, preferredLayoutAttributesFittingAttributes(_) lo tenés que usar solamente si tu celda no está hecha con constraints.

Y con respecto a estimatedItemSize, ahí podés tirar fruta. Esa info solamente se usa para estimar aproximadamente los largos de los scrolls.

ignaciovarela commented 8 years ago

Si no implemento preferredLayoutAttributesFittingAttributes no funciona.

Y necesito setearlo algo real al estimated, porque en preferredLayoutAttributesFittingAttributes el layoutAttributes.bounds.size es igual al estimated.

fedetrim commented 8 years ago

¿qué significa "no funciona"? ¿crashea?

ignaciovarela commented 8 years ago

Se queda infinitamente mostrando:

2016-09-06 18:16:09.293 CollectionViewComputedCellSizeExample[34218:771111] the behavior of the UICollectionViewFlowLayout is not defined because:
2016-09-06 18:16:09.294 CollectionViewComputedCellSizeExample[34218:771111] the item width must be less than the width of the UICollectionView minus the section insets left and right values, minus the content insets left and right values.
2016-09-06 18:16:09.294 CollectionViewComputedCellSizeExample[34218:771111] Please check the values return by the delegate.
2016-09-06 18:16:09.295 CollectionViewComputedCellSizeExample[34218:771111] The relevant UICollectionViewFlowLayout instance is <UICollectionViewFlowLayout: 0x7ff5a8f5dfa0>, and it is attached to <UICollectionView: 0x7ff5a982e800; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x7ff5a8f5d010>; layer = <CALayer: 0x7ff5a8f59c90>; contentOffset: {0, -64}; contentSize: {375, 76.5}> collection view layout: <UICollectionViewFlowLayout: 0x7ff5a8f5dfa0>.
2016-09-06 18:16:09.295 CollectionViewComputedCellSizeExample[34218:771111] Make a symbolic breakpoint at UICollectionViewFlowLayoutBreakForInvalidSizes to catch this in the debugger.

Cuando implemento preferredLayoutAttributesFittingAttributes como te mostré, se soluciona.

fedetrim commented 8 years ago

Entonces existe otro problema, pero te aseguro que esta es la posta. Tendría que verlo más en detalle para poder decirte exactamente qué está mal. Cuando me consiga un ratito me fijo y te aviso.

erolando commented 6 years ago

@ignaciovarela tuviste exito resolviendo el detalle?