前几天在用UICollectionView实现组合式布局的时候,想到如果在CollectionView上要求sticky header(也就是header自动悬停置顶)的效果应当如何实现。
我们都知道如果是在UITableView上实现非常简单,只要设置style为group然后多个section,headerView就会自动吸附在顶部。而collectionView虽然功能强大得多,但是原生并没有支持直接设置这个效果,所以需要自己实现。
作为一个懒人,首先想到的自然是站在巨人的肩膀上,CSStickyHeaderFlowLayout是一个比较知名的CollectionView布局控件,它支持sticky header效果以及头部的视差滚动效果,效果很赞。但是感觉稍微重了些,需要做更多的设置以及修改基类等。
然后我又找到了一篇文章,虽然已经是3年前的文章,但是写得非常简明清晰,而且阅读完有豁然开朗的感觉。
文章很短,全文如下:http://blog.radi.ws/post/32905838158/sticky-headers-for-uicollectionview-using
核心原理就是三句话:
The header should be positioned so it can never go further up than one header height above the first cell in the section.
The header should be positioned so it can never go further down than one header header height above the lower bounds of the last cell in the section.
The header should be positioned so it usually stays around the top edge, referencing the content offset of the collection view.
代码也很简单,只需要在UICollectionViewFlowLayout实现layoutAttributesForElementsInRect
和shouldInvalidateLayoutForBoundsChange
两个方法。
接着我附上文中源代码以及自己写的注释,穿插了一些对文章初略的翻译。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; UICollectionView * const cv = self.collectionView; CGPoint const contentOffset = cv.contentOffset;
NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet];
for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell) { [missingSections addIndex:layoutAttributes.indexPath.section]; } }
for (UICollectionViewLayoutAttributes *layoutAttributes in answer) { if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { [missingSections removeIndex:layoutAttributes.indexPath.section]; } }
[missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx];
UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
[answer addObject:layoutAttributes];
}];
for (UICollectionViewLayoutAttributes *layoutAttributes in answer) {
if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) {
NSInteger section = layoutAttributes.indexPath.section; NSInteger numberOfItemsInSection = [cv numberOfItemsInSection:section];
NSIndexPath *firstCellIndexPath = [NSIndexPath indexPathForItem:0 inSection:section]; NSIndexPath *lastCellIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section];
UICollectionViewLayoutAttributes *firstCellAttrs = [self layoutAttributesForItemAtIndexPath:firstCellIndexPath]; UICollectionViewLayoutAttributes *lastCellAttrs = [self layoutAttributesForItemAtIndexPath:lastCellIndexPath];
CGFloat headerHeight = CGRectGetHeight(layoutAttributes.frame); CGPoint origin = layoutAttributes.frame.origin; origin.y = MIN( MAX( contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight) ), (CGRectGetMaxY(lastCellAttrs.frame) - headerHeight) );
layoutAttributes.zIndex = 1024; layoutAttributes.frame = (CGRect){ .origin = origin, .size = layoutAttributes.frame.size }; } } return answer; }
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound { return YES; }
|
感觉又涨了姿势,撒花。