Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More changes! (features + performance + general improvements) #127

Merged
merged 21 commits into from
Apr 4, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3a9a929
Use @compatibility_alias instead of making dummy stub classes
Feb 8, 2012
e6b3c69
"row" backgrounds to have alternating patterns
Feb 9, 2012
baa4088
minimum press duration goes up by .005s and the number of times I acc…
Feb 9, 2012
46e4b90
Guard against inserting nil views
Feb 9, 2012
3f530fd
Rename clearSectionViews to clearViewsInArray, so it can keep being u…
Feb 9, 2012
f223a27
Ensure that a cell will never have a half a point frame
Feb 9, 2012
7f8bd8f
zeroIndexPath on KKGridView to avoid reallocating one every time a sc…
Feb 15, 2012
731cd2d
Delegate can disable selection by returning nil KKIndexPath.
zoul Feb 16, 2012
3d96726
Only call willSelectItemAtIndexPath: when the touches end.
zoul Feb 16, 2012
cddb8fb
be very lazy with selected background view by not generating a view u…
Feb 16, 2012
7d2bdb8
Never remove cells from superview; just set hidden and alpha to 0.
Feb 17, 2012
41d05ee
Merge branch 'master' of https://github.com/zoul/KKGridView
Feb 17, 2012
eb6acd0
Don't sort as often
Feb 17, 2012
ecf5c34
layout model cells after setting cell padding and cell size
Feb 21, 2012
9f4b963
dictionary instead of iterating through arrays as often for updates
Mar 16, 2012
5654062
__bridge is silly and irritating. go back to objc_unretainedPointer
Mar 18, 2012
099fb91
Use KVO instead of scrollview delegates
Mar 19, 2012
5e23e91
cancel highlighting when scrolling moves as well, to match tableview …
Mar 19, 2012
86398f4
Merge https://github.com/kolinkrewinkel/KKGridView
Mar 19, 2012
f2b2133
Don't set cell frames as often
Mar 20, 2012
9756e28
Merge
Apr 4, 2012
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions KKGridView/Definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@
#define __kk_weak __unsafe_unretained
#endif

static inline bool KKCGRectIntersectsRectVertically(CGRect rect1, CGRect rect2)
#if !defined(KKInline)
#define KKInline static __inline__ __attribute__((always_inline))
#endif

KKInline BOOL KKCGRectIntersectsRectVertically(CGRect rect1, CGRect rect2)
{
return (CGRectGetMinY(rect2) < CGRectGetMaxY(rect1)) && (CGRectGetMaxY(rect2) > CGRectGetMinY(rect1));
}

static inline bool KKCGRectIntersectsRectVerticallyWithPositiveNegativeMargin(CGRect rect1, CGRect rect2, CGFloat margin)
KKInline BOOL KKCGRectIntersectsRectVerticallyWithPositiveNegativeMargin(CGRect rect1, CGRect rect2, CGFloat margin)
{
return (CGRectGetMinY(rect2) - margin < CGRectGetMaxY(rect1)) && (CGRectGetMaxY(rect2) + margin > CGRectGetMinY(rect1));
}
Expand Down
1 change: 1 addition & 0 deletions KKGridView/KKGridView.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ typedef enum {
- (CGFloat)gridView:(KKGridView *)gridView heightForFooterInSection:(NSUInteger)section;
- (UIView *)gridView:(KKGridView *)gridView viewForHeaderInSection:(NSUInteger)section;
- (UIView *)gridView:(KKGridView *)gridView viewForFooterInSection:(NSUInteger)section;
- (UIView *)gridView:(KKGridView *)gridView viewForRow:(NSUInteger)row inSection:(NSUInteger)section; // a row is compromised of however many cells fit in a column of a given section
- (NSArray *)sectionIndexTitlesForGridView:(KKGridView *)gridView;
- (NSInteger)gridView:(KKGridView *)gridView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index;
@end
Expand Down
153 changes: 116 additions & 37 deletions KKGridView/KKGridView.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
struct KKSectionMetrics {
CGFloat footerHeight;
CGFloat headerHeight;
CGFloat rowHeight;
CGFloat sectionHeight;
NSUInteger itemCount;
};

@interface KKGridView () <UIGestureRecognizerDelegate,UIScrollViewDelegate> {
// View-wrapper containers
NSMutableArray *_footerViews;
NSMutableArray *_rowViews;
NSMutableArray *_headerViews;

// Metrics
Expand Down Expand Up @@ -59,6 +61,7 @@ @interface KKGridView () <UIGestureRecognizerDelegate,UIScrollViewDelegate> {
unsigned int heightForFooter:1;
unsigned int viewForHeader:1;
unsigned int viewForFooter:1;
unsigned int viewForRow:1;
unsigned int sectionIndexTitles:1;
unsigned int sectionForSectionIndexTitle:1;
} _dataSourceRespondsTo;
Expand Down Expand Up @@ -181,10 +184,8 @@ - (void)_sharedInitialization
self.delaysContentTouches = YES;
self.canCancelContentTouches = YES;

self.delegate = self;

_selectionRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(_handleSelection:)];
_selectionRecognizer.minimumPressDuration = 0.01;
_selectionRecognizer.minimumPressDuration = 0.015;
_selectionRecognizer.delegate = self;
_selectionRecognizer.cancelsTouchesInView = NO;
[self addGestureRecognizer:_selectionRecognizer];
Expand All @@ -196,12 +197,18 @@ - (void)_sharedInitialization
self.cellPadding = CGSizeMake(4.f, 4.f);
self.allowsMultipleSelection = NO;
self.backgroundColor = [UIColor whiteColor];


[self addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:NULL];
[self addObserver:self forKeyPath:@"tracking" options:NSKeyValueObservingOptionNew context:NULL];
}

#pragma mark - Cleanup

- (void)dealloc
{
[self removeObserver:self forKeyPath:@"contentOffset"];
[self removeObserver:self forKeyPath:@"tracking"];
[self removeGestureRecognizer:_selectionRecognizer];
[self _cleanupMetrics];
}
Expand All @@ -227,6 +234,7 @@ - (void)setDataSource:(id<KKGridViewDataSource>)dataSource
_dataSourceRespondsTo.heightForFooter = [_dataSource respondsToSelector:@selector(gridView:heightForFooterInSection:)];
_dataSourceRespondsTo.viewForHeader = [_dataSource respondsToSelector:@selector(gridView:viewForHeaderInSection:)];
_dataSourceRespondsTo.viewForFooter = [_dataSource respondsToSelector:@selector(gridView:viewForFooterInSection:)];
_dataSourceRespondsTo.viewForRow = [_dataSource respondsToSelector:@selector(gridView:viewForRow:inSection:)];
_dataSourceRespondsTo.sectionIndexTitles = [_dataSource respondsToSelector:@selector(sectionIndexTitlesForGridView:)];
_dataSourceRespondsTo.sectionForSectionIndexTitle = [_dataSource respondsToSelector:@selector(gridView:sectionForSectionIndexTitle:atIndex:)];
[self reloadData];
Expand Down Expand Up @@ -285,16 +293,22 @@ - (void)setAllowsMultipleSelection:(BOOL)allowsMultipleSelection

- (void)setCellPadding:(CGSize)cellPadding
{
_cellPadding = cellPadding;
if (_cellSize.width != 0.f && _cellSize.height != 0.f) {
if (!CGSizeEqualToSize(_cellPadding, cellPadding)) {
_cellPadding = cellPadding;

[self _layoutModelCells];

[self reloadData];
}
}

- (void)setCellSize:(CGSize)cellSize
{
_cellSize = cellSize;
if (_cellPadding.width != 0.f && _cellPadding.height != 0.f) {
if (!CGSizeEqualToSize(_cellSize, cellSize)) {
_cellSize = cellSize;

[self _layoutModelCells];

[self reloadData];
}
}
Expand Down Expand Up @@ -592,8 +606,10 @@ - (void)_cleanupCells
KKGridViewCell *cell = pair.cell;

[self _enqueueCell:cell withIdentifier:cell.reuseIdentifier];
cell.frame = (CGRect){.size = _cellSize};
[cell removeFromSuperview];
if (!CGSizeEqualToSize(_cellSize, cell.frame.size))
cell.frame = (CGRect){.size = _cellSize};
cell.hidden = YES;
cell.alpha = 0.;

[_visibleCells removeObjectForKey:pair.path];
}
Expand Down Expand Up @@ -643,6 +659,20 @@ - (CGFloat)_sectionHeightsCombinedUpToSection:(NSUInteger)section
return height;
}

- (CGFloat)_sectionHeightsCombinedUpToRow:(NSUInteger)row inSection:(NSUInteger)section
{
CGFloat height = 0.f;
for (NSUInteger index = 0; index < section && index < _metrics.count; index++) {
height += _metrics.sections[index].sectionHeight;
}

for (NSUInteger index = 0; index < row; index++) {
height += _metrics.sections[section].rowHeight;
}
return height;
}


#pragma mark - Cell Management

- (void)_displayCell:(KKGridViewCell *)cell atIndexPath:(KKIndexPath *)indexPath withAnimation:(KKGridViewAnimation)animation
Expand All @@ -666,10 +696,15 @@ - (void)_displayCell:(KKGridViewCell *)cell atIndexPath:(KKIndexPath *)indexPath
break;
}

if (_backgroundView)
[self insertSubview:cell aboveSubview:_backgroundView];
else
[self insertSubview:cell atIndex:0];
if (cell.superview) {
cell.hidden = NO;
cell.alpha = 1.;
} else {
if (_backgroundView)
[self insertSubview:cell atIndex:(_rowViews.count + 1)];
else
[self insertSubview:cell atIndex:_rowViews.count];
}

switch (animation) {
case KKGridViewAnimationExplode: {
Expand Down Expand Up @@ -769,13 +804,13 @@ - (CGRect)rectForCellAtIndexPath:(KKIndexPath *)indexPath
point.y += _metrics.sections[indexPath.section].headerHeight;
}

NSInteger row = floor(indexPath.index / _numberOfColumns);
NSInteger row = indexPath.index / _numberOfColumns;
NSInteger column = indexPath.index - (row * _numberOfColumns);

point.y += (row * (_cellSize.height + _cellPadding.height));
point.x += (column * (_cellSize.width + _cellPadding.width));

return (CGRect){point, _cellSize};
return CGRectIntegral((CGRect){point, _cellSize});
}

- (KKGridViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier
Expand All @@ -800,7 +835,7 @@ - (NSArray *)visibleIndexPaths
const CGRect visibleBounds = {self.contentOffset, self.bounds.size};
NSMutableArray *indexPaths = [[NSMutableArray alloc] initWithCapacity:12];

KKIndexPath *indexPath = [KKIndexPath indexPathForIndex:0 inSection:0];
KKIndexPath *indexPath = [KKIndexPath zeroIndexPath];

for (NSUInteger section = 0; section < _metrics.count; section++) {
indexPath.section = section;
Expand All @@ -820,7 +855,7 @@ - (NSArray *)visibleIndexPaths
return indexPaths;
}

#pragma mark - Model Modifiers
#pragma mark - Public Setters

- (void)_incrementCellsAtIndexPath:(KKIndexPath *)fromPath toIndexPath:(KKIndexPath *)toPath byAmount:(NSUInteger)amount negative:(BOOL)isNegative
{
Expand All @@ -840,7 +875,7 @@ - (void)_incrementCellsAtIndexPath:(KKIndexPath *)fromPath toIndexPath:(KKIndexP
[UIView animateWithDuration:KKGridViewDefaultAnimationDuration animations:^{
cell.alpha = 0.f;
} completion:^(BOOL finished) {
[cell removeFromSuperview];
cell.hidden = YES;
}];
}
if (!indexPathIsLessOrEqual || !lastPathIsGreatorOrEqual) {
Expand Down Expand Up @@ -974,7 +1009,8 @@ - (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
for (KKIndexPath *path in indexPaths) {
KKGridViewCell *cell = [_visibleCells objectForKey:path];
if (cell) {
[cell removeFromSuperview];
cell.hidden = YES;
cell.alpha = 0.;
[_visibleCells removeObjectForKey:path];
}

Expand All @@ -989,7 +1025,7 @@ - (void)_commonReload
{
[self reloadContentSize];

void (^clearSectionViews)(NSMutableArray *) = ^(NSMutableArray *views) {
void (^clearViewsInArray)(NSMutableArray *) = ^(NSMutableArray *views) {
for (id view in [views valueForKey:@"view"]) {
if (view != [NSNull null])
[view removeFromSuperview];
Expand All @@ -999,7 +1035,7 @@ - (void)_commonReload
};

if (_dataSourceRespondsTo.viewForHeader || _dataSourceRespondsTo.titleForHeader) {
clearSectionViews(_headerViews);
clearViewsInArray(_headerViews);
if (!_headerViews)
{
_headerViews = [[NSMutableArray alloc] initWithCapacity:_metrics.count];
Expand All @@ -1017,8 +1053,45 @@ - (void)_commonReload
}
}

if (_dataSourceRespondsTo.viewForRow) {
clearViewsInArray(_rowViews);
if (!_rowViews)
{
_rowViews = [[NSMutableArray alloc] initWithCapacity:_metrics.count];
}

for (NSUInteger section = 0; section < _metrics.count; section++) {
NSInteger previouslyCheckedRow = -1;

for (NSUInteger index = 0; index < _metrics.sections[section].itemCount; index++) {
NSInteger row = index / _numberOfColumns;

if (row <= previouslyCheckedRow)
continue;

previouslyCheckedRow = row;

UIView *view = [_dataSource gridView:self viewForRow:row inSection:section];
if (!view) {
continue;
}

KKGridViewRowBackground *rowBackground = [[KKGridViewRowBackground alloc] initWithView:view];
[_rowViews addObject:rowBackground];

CGFloat rowHeight = _cellSize.height + _cellPadding.height;
CGFloat position = [self _sectionHeightsCombinedUpToRow:row inSection:section] + _gridHeaderView.frame.size.height;
[self _configureSectionView:rowBackground inSection:section withStickPoint:position height:rowHeight];

if (_backgroundView)
[self insertSubview:rowBackground.view aboveSubview:_backgroundView];
else [self insertSubview:rowBackground.view atIndex:0];
}
}
}

if (_dataSourceRespondsTo.viewForFooter || _dataSourceRespondsTo.titleForFooter) {
clearSectionViews(_footerViews);
clearViewsInArray(_footerViews);
if (!_footerViews)
{
_footerViews = [[NSMutableArray alloc] initWithCapacity:_metrics.count];
Expand Down Expand Up @@ -1077,7 +1150,8 @@ - (void)reloadData
for (KKGridViewCell *cell in [_visibleCells allValues]) {
NSMutableSet *set = [self _reusableCellSetForIdentifier:cell.reuseIdentifier];
[set addObject:cell];
[cell removeFromSuperview];
cell.hidden = YES;
cell.alpha = 0.;
}

[_visibleCells removeAllObjects];
Expand Down Expand Up @@ -1110,12 +1184,12 @@ - (void)reloadContentSize
CGFloat heightForSection = 0.f;

struct KKSectionMetrics sectionMetrics = _metrics.sections[i];
_metrics.sections[i].rowHeight = (_cellSize.height + _cellPadding.height);

heightForSection += sectionMetrics.headerHeight + sectionMetrics.footerHeight;

NSUInteger numberOfRows = ceilf(sectionMetrics.itemCount / (float)_numberOfColumns);

heightForSection += numberOfRows * (_cellSize.height + _cellPadding.height);
heightForSection += numberOfRows * _metrics.sections[i].rowHeight;
heightForSection += (numberOfRows? _cellPadding.height:0.f);

_metrics.sections[i].sectionHeight = heightForSection;
Expand Down Expand Up @@ -1429,7 +1503,10 @@ - (void)_handleSelection:(UILongPressGestureRecognizer *)recognizer

KKIndexPath *indexPath = [self indexPathForItemAtPoint:locationInSelf];

if (indexPath.index == NSNotFound || indexPath.section == NSNotFound) {
if (state == UIGestureRecognizerStateEnded && _delegateRespondsTo.willSelectItem)
indexPath = [_gridDelegate gridView:self willSelectItemAtIndexPath:indexPath];

if (!indexPath || indexPath.index == NSNotFound || indexPath.section == NSNotFound) {
[self _cancelHighlighting];
return;
}
Expand Down Expand Up @@ -1458,19 +1535,21 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni
return (gestureRecognizer == _selectionRecognizer || otherGestureRecognizer == _selectionRecognizer);
}

#pragma mark - KVO

#pragma mark - UIScrollViewDelegate

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self _cancelHighlighting];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
_indexView.frame = (CGRect) {
{_indexView.frame.origin.x, scrollView.contentOffset.y},
_indexView.frame.size
};
if ([keyPath isEqualToString:@"contentOffset"]) {
_indexView.frame = (CGRect) {
{_indexView.frame.origin.x, self.contentOffset.y},
_indexView.frame.size
};
[self _cancelHighlighting];
} else if ([keyPath isEqualToString:@"tracking"]) {
if (self.tracking && !self.dragging) {
[self _cancelHighlighting];
}
}
}

#pragma mark - Animation Helpers
Expand Down
Loading