Set UICollectionViewFlowLayout to row layouts from bottom to top

By default (i.e., with a vertical scroll direction), UICollectionViewFlowLayout sets cells from the top left corner, moving from left to right until the line is full, and then moves down to the next line. Instead, I would like it to start in the lower left, go left to right until the line is full, and then go to the next line.

Is there an easy way to do this by subclassing UIScrollViewFlowLayout or do I basically need to reimplement this class from scratch?

The Apple documentation in the layout layout of the subclass suggests that I only need to override and reimplement my own version layoutAttributesForElementsInRect:, layoutAttributesForItemAtIndexPath:and collectionViewContentSize. But this does not seem simple. Since UICollectionViewFlowLayout does not reveal any grid layout calculations that it does inside prepareLayout, I need to infer all the layout values ​​needed for the layout from top to bottom from the values ​​that it generates for the layout from top to bottom.

I am not sure if this is possible. Although I can reuse my calculations about which groups of elements fall on the same lines, I will need to calculate the new y offsets. And for my calculations I need information about all the elements, but these methods of the superclass do not report this.

+4
source share
3 answers

You could basically implement it using simple logic, however this seems to be odd. If the contentview contentize is the same as for the borders of the collection, or if all the cells are visible, you can implement this with a simple flowLayout like this,

@implementation SimpleFlowLayout


- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
  UICollectionViewLayoutAttributes *attribute = [super layoutAttributesForItemAtIndexPath:indexPath];
  [self modifyLayoutAttribute:attribute];
  return attribute;
}

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect{
  NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
  for(UICollectionViewLayoutAttributes *attribute in attributes){
    [self modifyLayoutAttribute:attribute];
  }
  return attributes;
}


- (void)modifyLayoutAttribute:(UICollectionViewLayoutAttributes*)attribute{
  CGSize contentSize = self.collectionViewContentSize;
  CGRect frame = attribute.frame;
  frame.origin.x = contentSize.width - attribute.frame.origin.x - attribute.frame.size.width;
  frame.origin.y = contentSize.height - attribute.frame.origin.y - attribute.frame.size.height;
  attribute.frame = frame;

}

@end

So, the figure looks like this:

enter image description here

, , , , , , , . UICollectionView datasource, collectionView: cellForItemAtIndexPath: , indexPath, 1 --- 100, , . View, , 100 1 . , , .

+3

@insane-36 , collectionView.bounds == collectionView.collectionViewContentSize.

, collectionView.bounds < collectionViewcontentSize, , . , collectionView.bounds > collectionViewContentSize, collectionViewContentSize, , View ( , - , - UIScrollView).

, , : https://github.com/algal/ALGReversedFlowLayout.

+4

Reverse flow UICollectionView

import Foundation
import UIKit

class InvertedFlowLayout: UICollectionViewFlowLayout {
    let cellHeight: CGFloat = 120.0 // Your cell height here...

    override func prepare() {
        super.prepare()
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let attributesArray = super.layoutAttributesForElements(in: rect) else { return nil }
        var attributesArrayNew = [UICollectionViewLayoutAttributes]()

        for attribute in attributesArray {
            if attribute.representedElementKind == UICollectionView.elementKindSectionHeader || attribute.representedElementKind == UICollectionView.elementKindSectionFooter {
                let indexPath = IndexPath(item: attribute.indexPath.item, section: attribute.indexPath.section)
                let attributeKind = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: attribute.representedElementKind!, with: indexPath)

                var itemsCount = 0

                for i in 0 ..< attribute.indexPath.section + 1 {
                    itemsCount += self.collectionView!.numberOfItems(inSection: i)
                }

                attributeKind.frame = CGRect(x: 0, y: collectionViewContentSize.height - CGFloat(itemsCount) * (cellHeight + minimumLineSpacing) - CGFloat(attribute.indexPath.section + 1) * headerHeight(attribute.indexPath.section) - sectionInset.bottom + minimumLineSpacing, width: collectionViewContentSize.width, height: headerHeight(attribute.indexPath.section))
                attributesArrayNew.append(attributeKind)
            }
        }

        if let collectionView = self.collectionView {
            for section in 0 ..< collectionView.numberOfSections {
                for item in 0 ..< collectionView.numberOfItems(inSection: section) {
                    let indexPath = IndexPath(item: item, section: section)
                    if let attributeCell = layoutAttributesForItem(at: indexPath) {
                        if attributeCell.frame.intersects(rect) {
                            attributesArrayNew.append(attributeCell)
                        }
                    }
                }
            }
        }

        return attributesArrayNew
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attributeCell = UICollectionViewLayoutAttributes(forCellWith: indexPath)

        var itemsCount = 0

        for i in 0 ..< indexPath.section {
            itemsCount += self.collectionView!.numberOfItems(inSection: i)
        }

        attributeCell.frame = CGRect(x: 0, y: collectionViewContentSize.height - CGFloat(indexPath.item + 1 + itemsCount) * cellHeight - CGFloat(indexPath.item + itemsCount) * minimumLineSpacing - CGFloat(indexPath.section) * headerHeight(indexPath.section) - sectionInset.bottom, width: collectionViewContentSize.width, height: cellHeight)

        return attributeCell
    }

    func headerHeight(_ section: Int) -> CGFloat {
        if let collectionView = self.collectionView, let delegateFlowLayout = collectionView.delegate as? UICollectionViewDelegateFlowLayout {
            let size = delegateFlowLayout.collectionView!(collectionView, layout: self, referenceSizeForHeaderInSection: section)
            return size.height
        }

        return 0
    }

    override var collectionViewContentSize: CGSize {
        get {
            var height: CGFloat = 0.0
            var bounds = CGRect.zero

            if let collectionView = self.collectionView {
                for section in 0 ..< collectionView.numberOfSections {
                    let numItems = collectionView.numberOfItems(inSection: section)
                    height += CGFloat(numItems) * (cellHeight + minimumLineSpacing)
                }

                height += sectionInset.bottom + CGFloat(collectionView.numberOfSections) * headerHeight(0)
                bounds = collectionView.bounds
            }

            return CGSize(width: bounds.width, height: max(height, bounds.height))
        }
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        if let oldBounds = self.collectionView?.bounds,
            oldBounds.width != newBounds.width || oldBounds.height != newBounds.height {
            return true
        }

        return false
    }
}
0
source

Source: https://habr.com/ru/post/1540986/


All Articles