http://stackoverflow.com/questions/31203759/sticky-uicollectionview-header
Summary: Describes a solution creating sticky headers for UICollectionView
using UICollectionElementKindSectionHeader
-kind elements that scrolls with the content, which works like the standard UITableView
headers.
Congratulations if you’re using UICollectionView: you’re in a world of surprise and delight.
Default header and footer implementation in UICollectionViewFlowLayout
scrolls everything in one go, so headers may go off-screen as you scroll. These rules keep them on-screen:
- 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.
The solution is to implement a UICollectionViewFlowLayout
subclass and override two methods, -shouldInvalidateLayoutForBoundsChange:
and -layoutAttributesForElementsInRect:
.
When you set up the layout to be queried every time for a bounds change, it will be queried whenever the user scrolls, because scrolling in UIScrollView
is implemented by changing the origin
of the underlying bounds
of the layer.
It is also very important to override -layoutAttributesForElementsInRect:
, which serves two purposes. You must adjust layout attributes for each header element, and you need to manually insert attributes for missing headers because by default, UICollectionViewFlowLayout
only emits attributes for headers that are in the bounds. (UICollectionView
looks at all the attributes and maps them to existing or newly-created views.)
Here’s the code. Note that the zIndex
is raised for headers, which is important because now they are supposed to overlap the cells in the same section. Without the zIndex
configured, everything will be laid using the same zIndex
and the front/back order will be undefined.
- (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;
}
转自:http://blog.radi.ws/post/32905838158/sticky-headers-for-uicollectionview-using
参考: http://www.vigorouscoding.com/2013/03/uicollectionview-with-sticky-headers/