I\'m struggling to achieve a \"floating section header\" effect with UICollectionView
. Something that\'s been easy enough in UITableView
(default b
If already set flow layout in Storyboard
or Xib
file then try this,
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.sectionHeadersPinToVisibleBounds = true
I ran this with vigorouscoding's code. However that code did not consider sectionInset.
So I changed this code for vertical scroll
origin.y = MIN(
MAX(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight)),
(CGRectGetMaxY(lastCellAttrs.frame) - headerHeight)
);
to
origin.y = MIN(
MAX(contentOffset.y, (CGRectGetMinY(firstCellAttrs.frame) - headerHeight - self.sectionInset.top)),
(CGRectGetMaxY(lastCellAttrs.frame) - headerHeight + self.sectionInset.bottom)
);
If you guys want code for horizontal scroll, refer to code aove.
@iPrabu had an excellent answer with sectionHeadersPinToVisibleBounds
. I’ll just add that you can set this property in Interface Builder as well:
sectionHeadersPinToVisibleBounds
, type Boolean, and the checkbox checked.The default header view has a transparent background. You might want to make it (partially) opaque or add a blur effect view.
Swift 5.0
Put the following in your viewDidLoad:
if let layout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
layout.sectionHeadersPinToVisibleBounds = true
}
If you have a single header view that you want pinned to the top of your UICollectionView, here's a relatively simple way to do it. Note this is meant to be as simple as possible - it assumes you are using a single header in a single section.
//Override UICollectionViewFlowLayout class
@interface FixedHeaderLayout : UICollectionViewFlowLayout
@end
@implementation FixedHeaderLayout
//Override shouldInvalidateLayoutForBoundsChange to require a layout update when we scroll
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
//Override layoutAttributesForElementsInRect to provide layout attributes with a fixed origin for the header
- (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
NSMutableArray *result = [[super layoutAttributesForElementsInRect:rect] mutableCopy];
//see if there's already a header attributes object in the results; if so, remove it
NSArray *attrKinds = [result valueForKeyPath:@"representedElementKind"];
NSUInteger headerIndex = [attrKinds indexOfObject:UICollectionElementKindSectionHeader];
if (headerIndex != NSNotFound) {
[result removeObjectAtIndex:headerIndex];
}
CGPoint const contentOffset = self.collectionView.contentOffset;
CGSize headerSize = self.headerReferenceSize;
//create new layout attributes for header
UICollectionViewLayoutAttributes *newHeaderAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader withIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]];
CGRect frame = CGRectMake(0, contentOffset.y, headerSize.width, headerSize.height); //offset y by the amount scrolled
newHeaderAttributes.frame = frame;
newHeaderAttributes.zIndex = 1024;
[result addObject:newHeaderAttributes];
return result;
}
@end
See: https://gist.github.com/4613982
In case anybody is looking for a solution in Objective-C, put this in viewDidload:
UICollectionViewFlowLayout *flowLayout =
(UICollectionViewFlowLayout*)_yourcollectionView.collectionViewLayout;
[flowLayout setSectionHeadersPinToVisibleBounds:YES];