diff --git a/NHBalancedFlowLayout/NHBalancedFlowLayout.h b/NHBalancedFlowLayout/NHBalancedFlowLayout.h index 7d51164..c299f1d 100644 --- a/NHBalancedFlowLayout/NHBalancedFlowLayout.h +++ b/NHBalancedFlowLayout/NHBalancedFlowLayout.h @@ -21,6 +21,9 @@ // The preferred size for each row measured in the scroll direction @property (nonatomic) CGFloat preferredRowSize; +// Use this to stick the headers to the origin. +@property (nonatomic, assign) BOOL stickyHeaders; + // The size of each section's header. This maybe dynamically adjusted // per section via the protocol method referenceSizeForHeaderInSection. @property (nonatomic) CGSize headerReferenceSize; diff --git a/NHBalancedFlowLayout/NHBalancedFlowLayout.m b/NHBalancedFlowLayout/NHBalancedFlowLayout.m index 4df7fde..f86c928 100644 --- a/NHBalancedFlowLayout/NHBalancedFlowLayout.m +++ b/NHBalancedFlowLayout/NHBalancedFlowLayout.m @@ -68,7 +68,7 @@ - (void)initialize { // set to NULL so it is not released by accident in dealloc _itemFrameSections = NULL; - + self.stickyHeaders = NO; self.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10); self.minimumLineSpacing = 10; self.minimumInteritemSpacing = 10; @@ -194,6 +194,32 @@ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect } } + if (self.stickyHeaders) + { + NSMutableIndexSet *missingSections = [NSMutableIndexSet indexSet]; + for (NSUInteger idx = 0; idx < [layoutAttributes count]; idx++) + { + UICollectionViewLayoutAttributes *layoutAttribute = layoutAttributes[idx]; + + if (layoutAttribute.representedElementCategory == UICollectionElementCategoryCell) { + [missingSections addIndex:layoutAttribute.indexPath.section]; // remember that we need to layout header for this section + } + if ([layoutAttribute.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { + [layoutAttributes removeObjectAtIndex:idx]; // remove layout of header done by our super, we will do it right later + idx--; + } + } + + // layout all headers needed for the rect using self code + [missingSections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + + NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; + UICollectionViewLayoutAttributes *layoutAttribute = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; + [layoutAttributes addObject:layoutAttribute]; + }]; + } + + return layoutAttributes; } @@ -221,17 +247,49 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind attributes = nil; } + if (self.stickyHeaders && attributes && [kind isEqualToString:UICollectionElementKindSectionHeader]) + { + UIEdgeInsets const contentEdgeInsets = self.collectionView.contentInset; + CGPoint const contentOffset = CGPointMake(self.collectionView.contentOffset.x, + self.collectionView.contentOffset.y + contentEdgeInsets.top); + + CGPoint nextHeaderOrigin = CGPointMake(INFINITY, INFINITY); + + if (indexPath.section + 1 < [self.collectionView numberOfSections]) + { + UICollectionViewLayoutAttributes *nextHeaderAttributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:[NSIndexPath indexPathForItem:0 inSection:indexPath.section+1]]; + nextHeaderOrigin = nextHeaderAttributes.frame.origin; + } + + CGRect frame = attributes.frame; + if (self.scrollDirection == UICollectionViewScrollDirectionVertical) { + frame.origin.y = MIN(MAX(contentOffset.y, frame.origin.y), nextHeaderOrigin.y - CGRectGetHeight(frame)); + } + else { // UICollectionViewScrollDirectionHorizontal + frame.origin.x = MIN(MAX(contentOffset.x, frame.origin.x), nextHeaderOrigin.x - CGRectGetWidth(frame)); + } + attributes.zIndex = NSIntegerMax; + attributes.frame = frame; + + + } + return attributes; } - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds { + BOOL shouldInvalidateLayout = NO; CGRect oldBounds = self.collectionView.bounds; - if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds) || CGRectGetHeight(newBounds) != CGRectGetHeight(oldBounds)) { - return YES; + if (self.stickyHeaders) + { + shouldInvalidateLayout = YES; + } + else if (CGRectGetWidth(newBounds) != CGRectGetWidth(oldBounds) || CGRectGetHeight(newBounds) != CGRectGetHeight(oldBounds)) { + shouldInvalidateLayout = YES; } - return NO; + return shouldInvalidateLayout; } #pragma mark - Layout helpers diff --git a/NHBalancedFlowLayoutDemo/NHBalancedFlowLayoutDemo/ViewController.m b/NHBalancedFlowLayoutDemo/NHBalancedFlowLayoutDemo/ViewController.m index 69fcddc..f29f640 100644 --- a/NHBalancedFlowLayoutDemo/NHBalancedFlowLayoutDemo/ViewController.m +++ b/NHBalancedFlowLayoutDemo/NHBalancedFlowLayoutDemo/ViewController.m @@ -46,6 +46,7 @@ - (void)viewDidLoad [super viewDidLoad]; NHBalancedFlowLayout *layout = (NHBalancedFlowLayout *)self.collectionViewLayout; + layout.stickyHeaders = YES; layout.headerReferenceSize = CGSizeMake(HEADER_SIZE, HEADER_SIZE); layout.footerReferenceSize = CGSizeMake(FOOTER_SIZE, FOOTER_SIZE); } @@ -61,7 +62,7 @@ - (CGSize)collectionView:(UICollectionView *)collectionView layout:(NHBalancedFl - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { - return 1; + return 3; } - (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section;