I'm trying to implement a single sticky header in a UICollectionView
.
My sticky header behavior is a bit different than the usual one you can see e.g. in UITableView
. I have 3 headers in the collection view and I want only one of them to be sticky and stick to the top when the content is scrolled.
My code works pretty well. However, when I scroll down, the sticky header disappears suddenly at some point. Scrolling back makes the header appear again. What am I doing wrong?
I am attaching a implementation of my custom layout. It's a subclass of UICollectionViewFlowLayout
.
@implementation CustomFlowLayout
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
NSMutableArray *attributes = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
CGPoint const contentOffset = self.collectionView.contentOffset;
for (UICollectionViewLayoutAttributes *layoutAttributes in attributes)
{
// Adjust the sticky header frame.
if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader] &&
layoutAttributes.indexPath.section == SectionWithStickyHeadeIndex)
{
NSInteger numberOfItemsInSection = [self.collectionView numberOfItemsInSection:SectionWithStickyHeadeIndex];
NSIndexPath *firstObjectIndexPath = [NSIndexPath indexPathForItem:0
inSection:SectionWithStickyHeadeIndex];
UICollectionViewLayoutAttributes *firstObjectAttrs;
if (numberOfItemsInSection > 0)
{
firstObjectAttrs = [self layoutAttributesForItemAtIndexPath:firstObjectIndexPath];
}
else
{
firstObjectAttrs = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader
atIndexPath:firstObjectIndexPath];
}
CGPoint origin = layoutAttributes.frame.origin;
// Adjust the header origin so it sticks to the top.
origin.y = MAX(contentOffset.y + self.collectionView.contentInset.top,
CGRectGetMinY(firstObjectAttrs.frame) - CGRectGetHeight(layoutAttributes.frame));
layoutAttributes.zIndex = CGFLOAT_MAX;
layoutAttributes.frame = (CGRect)
{
.origin = origin,
.size = layoutAttributes.frame.size
};
break;
}
}
return attributes;
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound
{
return YES;
}
@end
I'm not 100% sure on this, but it looks like once you scrolled down far enough, the header's original position was no longer located inside the rect
argument. This caused the header's layout attributes to not be included in the attributes
array you iterated over in in the for
loop, resulting in the layout position no longer being adjusted to its "sticky" position at the top of the screen.
Try adding these lines right before the for
loop to add the sticky header's layout attributes to the attributes
array if they are not already there:
NSIndexPath *stickyHeaderIndexPath = [NSIndexPath indexPathForItem:0 inSection:SectionWithStickyHeaderIndex]; UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:stickyHeaderIndexPath]; if (![attributes containsObject:layoutAttributes]) { [attributes addObject:layoutAttributes]; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With