Skip to content

Instantly share code, notes, and snippets.

@vigorouscoding
Created March 13, 2013 20:10
Show Gist options
  • Star 30 You must be signed in to star a gist
  • Fork 14 You must be signed in to fork a gist
  • Save vigorouscoding/5155703 to your computer and use it in GitHub Desktop.
Save vigorouscoding/5155703 to your computer and use it in GitHub Desktop.
UICollectionView with sticky headers which works for horizontal as well as vertical scrolling
#import <UIKit/UIKit.h>
@interface DateFlowLayout : UICollectionViewFlowLayout
@end
@implementation DateFlowLayout
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.headerReferenceSize = CGSizeMake(50, 50);
self.minimumInteritemSpacing = 0;
self.minimumLineSpacing = 0;
self.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
return self;
}
-(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];
if (self.scrollDirection == UICollectionViewScrollDirectionVertical) {
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
};
} else {
CGFloat headerWidth = CGRectGetWidth(layoutAttributes.frame);
CGPoint origin = layoutAttributes.frame.origin;
origin.x = MIN(
MAX(contentOffset.x, (CGRectGetMinX(firstCellAttrs.frame) - headerWidth)),
(CGRectGetMaxX(lastCellAttrs.frame) - headerWidth)
);
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