Created
January 17, 2015 17:58
-
-
Save valvoline/3e1955a4492a897e677f to your computer and use it in GitHub Desktop.
Sticky Headers for UICollectionView using UICollectionViewFlowLayout that acts like Photo.app collection, taking care of edgeInsets
This file contains 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
// | |
// SpringboardLayout.h | |
// | |
// Created by valv0 on 11/10/14. | |
// Copyright (c) 2014 sofapps. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
@interface SpringboardLayout : UICollectionViewFlowLayout | |
@end |
This file contains 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
// | |
// SpringboardLayout.m | |
// | |
// Created by valv0 on 11/10/14. | |
// Copyright (c) 2014 sofapps. All rights reserved. | |
// | |
#import "SpringboardLayout.h" | |
@implementation SpringboardLayout | |
- (id)init | |
{ | |
if (self = [super init]) | |
{ | |
self.headerReferenceSize = CGSizeMake(0, 50); | |
self.footerReferenceSize = CGSizeMake(0, 0); | |
self.sectionInset = UIEdgeInsetsMake(10, 10, 80, 10); | |
self.scrollDirection = UICollectionViewScrollDirectionVertical; | |
self.minimumInteritemSpacing = 10; | |
self.minimumLineSpacing = 10; | |
if(IS_IPHONE_6 || IS_IPHONE_6PLUS) { | |
self.itemSize = CGSizeMake(100, 128); | |
} else { | |
self.itemSize = CGSizeMake(80, 108); | |
} | |
} | |
return self; | |
} | |
- (BOOL) shouldInvalidateLayoutForBoundsChange:(CGRect)newBound { | |
return YES; | |
} | |
- (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]; | |
} else 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; | |
origin.y = MIN(MAX(contentOffset.y + cv.contentInset.top, | |
(CGRectGetMinY(firstObjectAttrs.frame) - topHeaderHeight - self.sectionInset.top)) | |
,(CGRectGetMaxY(lastObjectAttrs.frame) - bottomHeaderHeight + self.sectionInset.bottom)); | |
layoutAttributes.zIndex = 1024; | |
layoutAttributes.frame = (CGRect){ | |
.origin = origin, | |
.size = layoutAttributes.frame.size | |
}; | |
} | |
} | |
return answer; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment