Consider a UICollectionView
with flow layout and horizontal direction. By default, cells are ordered from top to bottom, left to right. Like this:
1 4 7 10 13 16
2 5 8 11 14 17
3 6 9 12 15 18
In my case, the collection view is paged and it has been designed so that a specific number of cells fits in each page. Thus, a more natural ordering would be:
1 2 3 10 11 12
4 5 6 - 13 14 15
7 8 9 16 17 18
What would be the simplest to achieve this, short of implementing my own custom layout? In particular, I don't want to loose any of the functionalities that come for free with UICollectionViewFlowLayout
(such as insert/remove animations).
Or in general, how do you implement a reordering function f(n)
on a flow layout? The same could be applicable to a right-to-left ordering, for example.
My approach so far
My first approach was to subclass UICollectionViewFlowLayout
and override layoutAttributesForItemAtIndexPath:
:
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *reorderedIndexPath = [self reorderedIndexPathOfIndexPath:indexPath];
UICollectionViewLayoutAttributes *layout = [super layoutAttributesForItemAtIndexPath:reorderedIndexPath];
layout.indexPath = indexPath;
return layout;
}
Where reorderedIndexPathOfIndexPath:
is f(n)
. By calling super
, I don't have to calculate the layout of each element manually.
Additionally, I had to override layoutAttributesForElementsInRect:
, which is the method the layout uses to choose which elements to display.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *result = [NSMutableArray array];
NSInteger sectionCount = 1;
if ([self.collectionView.dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)])
{
sectionCount = [self.collectionView.dataSource numberOfSectionsInCollectionView:self.collectionView];
}
for (int s = 0; s < sectionCount; s++)
{
NSInteger itemCount = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:s];
for (int i = 0; i < itemCount; i++)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s];
UICollectionViewLayoutAttributes *layout = [self layoutAttributesForItemAtIndexPath:indexPath];
if (CGRectIntersectsRect(rect, layout.frame))
{
[result addObject:layout];
}
}
}
return result;
}
Here I just try every element and if it is within the given rect
, I return it.
If this approach is the way to go, I have the following more specific questions:
- Is there any way I can simplify the
layoutAttributesForElementsInRect:
override, or make it more efficient?
- Am I missing something? At the very least swapping cells of different pages produces odd results. I suspect it's related to
initialLayoutAttributesForAppearingItemAtIndexPath:
and finalLayoutAttributesForDisappearingItemAtIndexPath:
, but I can't pinpoint exactly what is the problem.
- In my case,
f(n)
depends on the number of columns and rows of each page. Is there any way of extracting this information from UICollectionViewFlowLayout
, short of hardcoding it myself? I thought of querying layoutAttributesForElementsInRect:
with the bounds of the collection view, and deducing the rows and columns from there, but this also feels inefficient.
See Question&Answers more detail:
os