Forked from toblerpwn/CustomCollectionFlowLayout.h
Last active
February 12, 2016 04:12
-
-
Save haashem/e6a00e90bc5ee592b5fe to your computer and use it in GitHub Desktop.
Sticky Headers at the top of a UICollectionView! -- // -- http://stackoverflow.com/questions/13511733/how-to-make-supplementary-view-float-in-uicollectionview-as-section-headers-do-i -- // -- still needs work around contentInsets.bottom and oddly-sized footers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CustomCollectionFlowLayout.h | |
// evilapples | |
// | |
// http://stackoverflow.com/questions/13511733/how-to-make-supplementary-view-float-in-uicollectionview-as-section-headers-do-i | |
// | |
// | |
#import <UIKit/UIKit.h> | |
@interface CustomCollectionFlowLayout : UICollectionViewFlowLayout | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// CustomCollectionFlowLayout.m | |
// evilapples | |
// | |
// http://stackoverflow.com/questions/13511733/how-to-make-supplementary-view-float-in-uicollectionview-as-section-headers-do-i | |
// | |
// | |
#import "CustomCollectionFlowLayout.h" | |
@implementation CustomCollectionFlowLayout | |
- (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 *firstObjectIndexPath = [NSIndexPath indexPathForItem:0 inSection:section]; | |
NSIndexPath *lastObjectIndexPath = [NSIndexPath indexPathForItem:MAX(0, (numberOfItemsInSection - 1)) inSection:section]; | |
BOOL cellsExist; | |
UICollectionViewLayoutAttributes *firstObjectAttrs; | |
UICollectionViewLayoutAttributes *lastObjectAttrs; | |
if (numberOfItemsInSection > 0) { // use cell data if items exist | |
cellsExist = YES; | |
firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath]; | |
lastObjectAttrs = [self layoutAttributesForItemAtIndexPath:lastObjectIndexPath]; | |
} else { // else use the header and footer | |
cellsExist = NO; | |
firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader | |
atIndexPath:firstObjectIndexPath]; | |
lastObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter | |
atIndexPath:lastObjectIndexPath]; | |
} | |
CGFloat topHeaderHeight = (cellsExist) ? CGRectGetHeight(layoutAttributes.frame) : 0; | |
CGFloat bottomHeaderHeight = CGRectGetHeight(layoutAttributes.frame); | |
CGRect frameWithEdgeInsets = UIEdgeInsetsInsetRect(layoutAttributes.frame, | |
cv.contentInset); | |
CGPoint origin = frameWithEdgeInsets.origin; | |
// we don't want header be positioned out of CV bounds, we we check if lastObjectAttrs exists, else section header origin will be negative | |
if (lastObjectAttrs) { | |
origin.y = MIN( | |
MAX( | |
contentOffset.y + cv.contentInset.top, | |
(CGRectGetMinY(firstObjectAttrs.frame) - topHeaderHeight) | |
), | |
(CGRectGetMaxY(lastObjectAttrs.frame) - bottomHeaderHeight) | |
); | |
} | |
layoutAttributes.zIndex = 1024; | |
layoutAttributes.frame = (CGRect){ | |
.origin = origin, | |
.size = layoutAttributes.frame.size | |
}; | |
} | |
} | |
return answer; | |
} | |
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound { | |
return YES; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment