Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Configure UICollectionViewFlowLayout to lay out rows from bottom to top

By default (i.e., with a vertical scrolling direction), the UICollectionViewFlowLayout lays out cells by starting at the top-left, going from left to right, until the row is filled, and then proceeds to the next line down. Instead, I would like it to start at the bottom-left, go from left to right, until the row is filled, and then proceed to the next line up.

Is there a straightforward way to do this by subclassing UIScrollViewFlowLayout, or do I basically need to re-implement that class from scratch?

Apple's documentation on subclassing flow layout suggests that I only need to override and re-implement my own version of layoutAttributesForElementsInRect:, layoutAttributesForItemAtIndexPath:, and collectionViewContentSize. But this does not seem straightforward. Since UICollectionViewFlowLayout does not expose any of the grid layout calculations it makes internally in prepareLayout, I need to deduce all the layout values needed for the bottom-to-top layout from the values it generates for a top-to-bottom layout.

I am not sure this is possible. While I can re-use its calculations about which groups of items get put on the same rows, I will need to calculate new y offsets. And to make my calculations I will need information about all the items, but those superclass methods do not report that.

like image 381
algal Avatar asked May 17 '14 20:05

algal


2 Answers

The very helpful answer by @insane-36 showed a way to do it when collectionView.bounds == collectionView.collectionViewContentSize.

But if you wish to support the case where collectionView.bounds < collectionViewcontentSize, then I believe you need to re-map the rects exactly to support scrolling properly. If you wish to support the case where collectionView.bounds > collectionViewContentSize, then you need to override collectionViewContentSize to ensure the content rect is positioned at the bottom of the collectionView (since otherwise it will be positioned at the top, due to the top-to-bottom default behavior of UIScrollView).

So the full solution is a bit more involved, and I ended up developing it here: https://github.com/algal/ALGReversedFlowLayout.

like image 53
algal Avatar answered Sep 20 '22 13:09

algal


You could basically implement it with a simple logic, however this seems to be some how odd. If the collectionview contentsize is same as that of the collectionview bounds or if all the cells are visible then you could implement this with simple flowLayout as 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

And so the figure looks like this,

enter image description here

But, if you use more rows, more than the that can be seen on the screen at the same time, then there seems to be some problem with reusing. Since the UICollectionView datasource method, collectionView:cellForItemAtIndexPath: works linearly and asks for the indexPath as the user scrolls, the cell are asked in the usual increasing indexPath pattern such as 1 --- 100 though we would want it to reverse this pattern. While scrolling we would need the collectionView to ask us for the items in decreasing order since our 100 item resides at top and 1 item at bottom. So, I dont have any particular idea about how this could be accomplished.

like image 30
Sandeep Avatar answered Sep 20 '22 13:09

Sandeep