From 38e8b888bd1d1d11f1d6245a240b970e637107e9 Mon Sep 17 00:00:00 2001 From: catlan Date: Sun, 9 May 2010 23:19:55 +0800 Subject: [PATCH 01/10] add selectionGlowShadowRadius property --- Classes/AQGridViewCell.h | 2 ++ Classes/AQGridViewCell.m | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Classes/AQGridViewCell.h b/Classes/AQGridViewCell.h index 8020cf0..a577ff5 100644 --- a/Classes/AQGridViewCell.h +++ b/Classes/AQGridViewCell.h @@ -65,6 +65,7 @@ typedef enum { UIColor * _backgroundColor; UIColor * _separatorColor; UIColor * _selectionGlowColor; + CGFloat _selectionGlowShadowRadius; UIView * _bottomSeparatorView; UIView * _rightSeparatorView; NSTimer * _fadeTimer; @@ -103,6 +104,7 @@ typedef enum { @property (nonatomic, getter=isSelected) BOOL selected; // default is NO @property (nonatomic, getter=isHighlighted) BOOL highlighted; // default is NO @property (nonatomic, retain) UIColor * selectionGlowColor; // default is dark grey, ignored if selectionStyle != AQGridViewCellSelectionStyleGlow +@property (nonatomic) CGFloat selectionGlowShadowRadius; // default is 12.0, ignored if selectionStyle != AQGridViewCellSelectionStyleGlow // this can be overridden by subclasses to return a subview's layer to which to add the glow // the default implementation returns the contentView's layer diff --git a/Classes/AQGridViewCell.m b/Classes/AQGridViewCell.m index 06823d9..0fc8f2a 100644 --- a/Classes/AQGridViewCell.m +++ b/Classes/AQGridViewCell.m @@ -49,6 +49,7 @@ @implementation AQGridViewCell @synthesize contentView=_contentView, backgroundView=_backgroundView, selectedBackgroundView=_selectedBackgroundView; @synthesize reuseIdentifier=_reuseIdentifier, selectionGlowColor=_selectionGlowColor; +@synthesize selectionGlowShadowRadius=_selectionGlowShadowRadius; - (id) initWithFrame: (CGRect) frame reuseIdentifier: (NSString *) reuseIdentifier { @@ -68,6 +69,8 @@ - (id) initWithFrame: (CGRect) frame reuseIdentifier: (NSString *) reuseIdentifi _selectionColorInfo = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); self.backgroundColor = [UIColor whiteColor]; + _selectionGlowShadowRadius = 12.0f; + return ( self ); } @@ -457,7 +460,7 @@ - (void) setHighlighted: (BOOL) value animated: (BOOL) animated else theLayer.shadowColor = [[UIColor darkGrayColor] CGColor]; - theLayer.shadowRadius = 12.0; + theLayer.shadowRadius = self.selectionGlowShadowRadius; // add or remove the 'shadow' as appropriate if ( value ) From 2381248aca6104e2e04c9a66fdc2b2c4c7cbff5e Mon Sep 17 00:00:00 2001 From: catlan Date: Sun, 9 May 2010 23:38:06 +0800 Subject: [PATCH 02/10] FIX: gridView isn't scrollable when the number of items fits into its view area --- Classes/AQGridViewData.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Classes/AQGridViewData.m b/Classes/AQGridViewData.m index 7c418d5..286d17a 100644 --- a/Classes/AQGridViewData.m +++ b/Classes/AQGridViewData.m @@ -145,8 +145,11 @@ - (CGSize) sizeForEntireGrid if ( _numberOfItems % numPerRow != 0 ) numRows++; - return ( CGSizeMake(((CGFloat)ceilf(_actualCellSize.width * numPerRow)) + _leftPadding + _rightPadding, - ((CGFloat)ceilf((CGFloat)numRows * _actualCellSize.height)) + _topPadding + _bottomPadding) ); + CGFloat height = ( ((CGFloat)ceilf((CGFloat)numRows * _actualCellSize.height)) + _topPadding + _bottomPadding ); + if (height < _gridView.bounds.size.height) + height = _gridView.bounds.size.height + 1; + + return ( CGSizeMake(((CGFloat)ceilf(_actualCellSize.width * numPerRow)) + _leftPadding + _rightPadding, height) ); } - (NSUInteger) numberOfItemsPerRow From 09c2f466b496e00cd14ed247d6fd6a14fc3ffc75 Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Mon, 10 May 2010 17:32:06 -0400 Subject: [PATCH 03/10] Fixed a bug in the license. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index e130ad6..751bed1 100644 --- a/LICENSE +++ b/LICENSE @@ -5,5 +5,5 @@ Redistribution and use in source and binary forms, with or without modification, Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +Neither the name of Kobo Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From dbf20841b7868ee4f1b1b1adeea3b4c134ba88c6 Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Tue, 11 May 2010 09:39:26 -0400 Subject: [PATCH 04/10] Return NSNotFound when asked for the index of a grid cell at a point outside the bounds of the grid. --- Classes/AQGridViewData.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Classes/AQGridViewData.m b/Classes/AQGridViewData.m index 286d17a..5555e1f 100644 --- a/Classes/AQGridViewData.m +++ b/Classes/AQGridViewData.m @@ -96,7 +96,11 @@ - (NSUInteger) itemIndexForPoint: (CGPoint) point NSUInteger x = (NSUInteger)floorf(point.x); NSUInteger col = x / (NSUInteger)_actualCellSize.width; - return ( (row * [self numberOfItemsPerRow]) + col ); + NSUInteger result = (row * [self numberOfItemsPerRow]) + col; + if ( result >= self.numberOfItems ) + return ( NSNotFound ); + + return ( result ); } - (CGRect) cellRectForPoint: (CGPoint) point From c33a2ecc3df08b9948042010c917773806eca219 Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Tue, 11 May 2010 09:40:19 -0400 Subject: [PATCH 05/10] Handle the case where the dragged-to cell index isn't in the grid by snapping the drag target back to the last place in the grid. --- .../SpringBoard/Classes/SpringBoardViewController.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Examples/SpringBoard/Classes/SpringBoardViewController.m b/Examples/SpringBoard/Classes/SpringBoardViewController.m index bc54447..e2fea30 100644 --- a/Examples/SpringBoard/Classes/SpringBoardViewController.m +++ b/Examples/SpringBoard/Classes/SpringBoardViewController.m @@ -199,6 +199,11 @@ - (void) moveActionGestureRecognizerStateChanged: (UIGestureRecognizer *) recogn { CGPoint p = [recognizer locationInView: _gridView]; NSUInteger index = [_gridView indexForItemAtPoint: p]; + if ( index == NSNotFound ) + { + // index is the last available location + index = [_icons count] - 1; + } // update the data store id obj = [[_icons objectAtIndex: _dragOriginIndex] retain]; @@ -282,6 +287,14 @@ - (void) moveActionGestureRecognizerStateChanged: (UIGestureRecognizer *) recogn // update empty cell to follow, if necessary NSUInteger index = [_gridView indexForItemAtPoint: [recognizer locationInView: _gridView]]; + + // don't do anything if it's over an unused grid cell + if ( index == NSNotFound ) + { + // snap back to the last possible index + index = [_icons count] - 1; + } + if ( index != _emptyCellIndex ) { NSLog( @"Moving empty cell from %u to %u", _emptyCellIndex, index ); From 734710bb64845a64f08d93a4e44218e8ffc99028 Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Wed, 26 May 2010 14:21:59 -0400 Subject: [PATCH 06/10] Fixed a bug where -[AQGridViewData copyWithZone:] wasn't copying left or right padding, causing layout issues after animated insertion/deletion. --- Classes/AQGridViewData.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Classes/AQGridViewData.m b/Classes/AQGridViewData.m index 5555e1f..2b6ed1b 100644 --- a/Classes/AQGridViewData.m +++ b/Classes/AQGridViewData.m @@ -65,6 +65,8 @@ - (id) copyWithZone: (NSZone *) zone theCopy->_layoutDirection = _layoutDirection; theCopy->_topPadding = _topPadding; theCopy->_bottomPadding = _bottomPadding; + theCopy->_leftPadding = _leftPadding; + theCopy->_rightPadding = _rightPadding; theCopy->_numberOfItems = _numberOfItems; theCopy->_reorderedIndex = _reorderedIndex; return ( theCopy ); From 4f902769a76dd408c4a4196e5854f7a32371287c Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Wed, 26 May 2010 14:48:02 -0400 Subject: [PATCH 07/10] Fixed layout issues for grid bounds changes while looking at bottom of grid view. Fixed issue where old cells might be left in pre-animation positions instead of being removed from the grid view. Fixed an issue where Insert could cause the last item to briefly jump to its new location before being animated there from its old location. Selection and highlighting are now much more consistent. Added support for -touchesMoved: being less sensitive if a gesture recognizer is attached to the grid view and might potentially match the touch. Special orientation change handling is no longer necessary-- commented out that code for now. Highlighting cells now better handles multiple stage highlight/select to avoid keeping text highlighted when the deselected. Cell deselection animation for non-glow now properly matches that of UITableViewCell. --- Classes/AQGridView.m | 125 +++++++++++++++++++++++++++++++-------- Classes/AQGridViewCell.m | 25 ++++---- Classes/AQGridViewData.m | 2 +- 3 files changed, 117 insertions(+), 35 deletions(-) diff --git a/Classes/AQGridView.m b/Classes/AQGridView.m index 9ecfc44..8812c95 100644 --- a/Classes/AQGridView.m +++ b/Classes/AQGridView.m @@ -406,11 +406,26 @@ - (void) updateContentRectWithOldMaxLocation: (CGPoint) oldMaxLocation gridSize: - (void) handleGridViewBoundsChanged: (CGRect) oldBounds toNewBounds: (CGRect) bounds { + CGSize oldGridSize = [_gridData sizeForEntireGrid]; + BOOL wasAtBottom = CGRectGetMaxY(oldBounds) == oldGridSize.height; + [_gridData gridViewDidChangeBoundsSize: bounds.size]; _flags.numColumns = [_gridData numberOfItemsPerRow]; + CGSize newGridSize = [_gridData sizeForEntireGrid]; CGPoint oldMaxLocation = CGPointMake(CGRectGetMaxX(oldBounds), CGRectGetMaxY(oldBounds)); - [self updateContentRectWithOldMaxLocation: oldMaxLocation gridSize: [_gridData sizeForEntireGrid]]; + [self updateContentRectWithOldMaxLocation: oldMaxLocation gridSize: newGridSize]; + + if ( (wasAtBottom) && (newGridSize.height > oldGridSize.height) ) + { + CGRect contentRect = self.bounds; + if ( CGRectGetMaxY(contentRect) < newGridSize.height ) + { + contentRect.origin.y += (newGridSize.height - oldGridSize.height); + self.contentOffset = contentRect.origin; + } + } + [self updateVisibleGridCellsNow]; _flags.allCellsNeedLayout = 1; } @@ -696,6 +711,19 @@ - (void) fixCellsFromAnimation self.animatingCells = nil; _revealingIndices.length = _revealingIndices.location = 0; + NSMutableSet * removals = [[NSMutableSet alloc] init]; + for ( UIView * view in self.subviews ) + { + if ( [view isKindOfClass: [KBGridViewCell class]] == NO ) + continue; + + if ( [_visibleCells containsObject: view] == NO ) + [removals addObject: view]; + } + + [removals makeObjectsPerformSelector: @selector(removeFromSuperview)]; + [removals release]; + // update the content size/offset based on the new grid data CGPoint oldMaxLocation = CGPointMake(CGRectGetMaxX(self.bounds), CGRectGetMaxY(self.bounds)); [self updateContentRectWithOldMaxLocation: oldMaxLocation gridSize: [_gridData sizeForEntireGrid]]; @@ -718,6 +746,8 @@ - (void) endUpdateAnimations if ( _updateInfo.numberOfUpdates == 0 ) { //_reloadingSuspendedCount--; + _flags.isAnimatingUpdates = 0; + _flags.updating = 0; [_updateInfo release]; _updateInfo = nil; return; @@ -818,26 +848,6 @@ - (NSUInteger) indexOfSelectedItem return ( _selectedIndex ); } -- (void) selectItemAtIndex: (NSUInteger) index animated: (BOOL) animated - scrollPosition: (AQGridViewScrollPosition) scrollPosition -{ - if ( _selectedIndex != NSNotFound ) - [self deselectItemAtIndex: _selectedIndex animated: NO]; - - _selectedIndex = index; - [self scrollToItemAtIndex: index atScrollPosition: AQGridViewScrollPositionNone animated: animated]; -} - -- (void) deselectItemAtIndex: (NSUInteger) index animated: (BOOL) animated -{ - AQGridViewCell * cell = [self cellForItemAtIndex: index]; - if ( cell != nil ) - [cell setSelected: NO animated: animated]; - - if ( _selectedIndex == index ) - _selectedIndex = NSNotFound; -} - - (void) highlightItemAtIndex: (NSUInteger) index animated: (BOOL) animated scrollPosition: (AQGridViewScrollPosition) position { if ( [_highlightedIndices containsIndex: index] ) @@ -875,6 +885,11 @@ - (void) unhighlightItemAtIndex: (NSUInteger) index animated: (BOOL) animated return; [_highlightedIndices removeIndex: index]; + + // don't remove highlighting if the cell is actually the selected cell + if ( index == _selectedIndex ) + return; + AQGridViewCell * cell = [self cellForItemAtIndex: index]; if ( cell != nil ) [cell setHighlighted: NO animated: animated]; @@ -908,7 +923,7 @@ - (void) _selectItemAtIndex: (NSUInteger) index animated: (BOOL) animated return; // already selected this item if ( _selectedIndex != NSNotFound ) - [self _deselectItemAtIndex: _selectedIndex animated: animated notifyDelegate: NO]; + [self _deselectItemAtIndex: _selectedIndex animated: animated notifyDelegate: notifyDelegate]; if ( _flags.allowsSelection == 0 ) return; @@ -930,6 +945,20 @@ - (void) _selectItemAtIndex: (NSUInteger) index animated: (BOOL) animated if ( notifyDelegate && _flags.delegateDidSelectItem ) [self.delegate gridView: self didSelectItemAtIndex: index]; + + // ensure that the selected item is no longer marked as just 'highlighted' (that's an intermediary state) + [_highlightedIndices removeIndex: index]; +} + +- (void) selectItemAtIndex: (NSUInteger) index animated: (BOOL) animated + scrollPosition: (AQGridViewScrollPosition) scrollPosition +{ + [self _selectItemAtIndex: index animated: animated scrollPosition: scrollPosition notifyDelegate: NO]; +} + +- (void) deselectItemAtIndex: (NSUInteger) index animated: (BOOL) animated +{ + [self _deselectItemAtIndex: index animated: animated notifyDelegate: NO]; } #pragma mark - @@ -1028,6 +1057,43 @@ - (void) _userSelectItemAtIndex: (NSNumber *) indexNum _pendingSelectionIndex = NSNotFound; } +- (BOOL) _gestureRecognizerIsHandlingTouches: (NSSet *) touches +{ + // see if the touch is (possibly) being tracked by a gesture recognizer + for ( id recognizer in self.gestureRecognizers ) + { + switch ( [recognizer state] ) + { + case UIGestureRecognizerStateEnded: + case UIGestureRecognizerStateCancelled: + case UIGestureRecognizerStateFailed: + continue; + + default: + break; + } + + if ( [recognizer numberOfTouches] == [touches count] ) + { + // simple version: + // pick a touch from our event's set, and see if it's in the recognizer's set + UITouch * touch = [touches anyObject]; + CGPoint touchLocation = [touch locationInView: self]; + + for ( NSUInteger i = 0; i < [recognizer numberOfTouches]; i++ ) + { + CGPoint test = [recognizer locationOfTouch: i inView: self]; + if ( CGPointEqualToPoint(test, touchLocation) ) + { + return ( YES ); + } + } + } + } + + return ( NO ); +} + - (void) touchesBegan: (NSSet *) touches withEvent: (UIEvent *) event { _flags.ignoreTouchSelect = ([self isDragging] ? 1 : 0); @@ -1079,12 +1145,20 @@ - (void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event { if ( _flags.ignoreTouchSelect == 0 ) { + Class cls = NSClassFromString(@"UILongPressGestureRecognizer"); + if ( (cls != Nil) && ([cls instancesRespondToSelector: @selector(setNumberOfTouchesRequired:)]) ) + { + if ( [self _gestureRecognizerIsHandlingTouches: touches] ) + goto passToSuper; // I feel all icky now + } + [self _cancelContentTouchUsingEvent: event forced: NO]; [self highlightItemAtIndex: NSNotFound animated: NO scrollPosition: AQGridViewScrollPositionNone]; _flags.ignoreTouchSelect = 1; _touchedContentView = nil; } +passToSuper: [super touchesMoved: touches withEvent: event]; } @@ -1179,6 +1253,9 @@ - (void) updateVisibleGridCellsNow if ( _reloadingSuspendedCount > 0 ) return; + if ( _flags.isAnimatingUpdates || _flags.updating ) + return; + _reloadingSuspendedCount++; NSIndexSet * newVisibleIndices = [_gridData indicesOfCellsInRect: [self gridViewVisibleBounds]]; @@ -1455,8 +1532,8 @@ - (void) viewWillRotateToInterfaceOrientation: (UIInterfaceOrientation) orientat // to avoid cell pop-in or pop-out: // if we're switching to landscape, don't update cells until after the transition. // if we're switching to portrait, update cells first. - if ( UIInterfaceOrientationIsLandscape(orientation) ) - _reloadingSuspendedCount++; + //if ( UIInterfaceOrientationIsLandscape(orientation) ) + // _reloadingSuspendedCount++; } - (void) viewDidRotate diff --git a/Classes/AQGridViewCell.m b/Classes/AQGridViewCell.m index 0fc8f2a..3bc609b 100644 --- a/Classes/AQGridViewCell.m +++ b/Classes/AQGridViewCell.m @@ -259,10 +259,16 @@ - (void) highlightSubviewsOfView: (UIView *) aView CFDictionarySetValue( _selectionColorInfo, view, info ); } - id value = [view valueForKey: @"highlighted"]; - if ( value == nil ) - value = [NSNumber numberWithBool: NO]; - [info setObject: value forKey: @"highlighted"]; + // don't overwrite any prior cache of a view's original highlighted state. + // this is because 'highlighted' will be set, then 'selected', which can perform 'highlight' again before the animation completes + if ( [info objectForKey: @"highlighted"] == nil ) + { + id value = [view valueForKey: @"highlighted"]; + if ( value == nil ) + value = [NSNumber numberWithBool: NO]; + [info setObject: value forKey: @"highlighted"]; + } + [view setValue: [NSNumber numberWithBool: YES] forKey: @"highlighted"]; } @@ -343,12 +349,6 @@ - (void) _beginBackgroundHighlight: (BOOL) highlightOn animated: (BOOL) animated } else { - [UIView setAnimationsEnabled: NO]; - // find all non-opaque subviews and make opaque again, with original background colors - [self makeSubviewsOfViewOpaqueAgain: self]; - [UIView setAnimationsEnabled: YES]; - - // now we're animating once more _selectedBackgroundView.alpha = 0.0; } @@ -395,6 +395,11 @@ - (void) highlightAnimationStopped: (NSString * __unused) animationID context: ( } else { + [UIView setAnimationsEnabled: NO]; + // find all non-opaque subviews and make opaque again, with original background colors + [self makeSubviewsOfViewOpaqueAgain: self]; + [UIView setAnimationsEnabled: YES]; + _cellFlags.highlighted = 0; [_selectedBackgroundView removeFromSuperview]; CFDictionaryRemoveAllValues( _selectionColorInfo ); diff --git a/Classes/AQGridViewData.m b/Classes/AQGridViewData.m index 2b6ed1b..cf0ed71 100644 --- a/Classes/AQGridViewData.m +++ b/Classes/AQGridViewData.m @@ -100,7 +100,7 @@ - (NSUInteger) itemIndexForPoint: (CGPoint) point NSUInteger result = (row * [self numberOfItemsPerRow]) + col; if ( result >= self.numberOfItems ) - return ( NSNotFound ); + result = NSNotFound; return ( result ); } From 31e7ff6dcd7574aba5d61c2ed28105970502c63b Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Wed, 26 May 2010 15:08:52 -0400 Subject: [PATCH 08/10] Now raises an exception in -endUpdates if the expected new item count doesn't match the data source's value. Fixed a typo (grr). --- Classes/AQGridView.m | 18 +++++++++++++++++- Classes/AQGridViewUpdateInfo.h | 1 + Classes/AQGridViewUpdateInfo.m | 7 ++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Classes/AQGridView.m b/Classes/AQGridView.m index 8812c95..19a1ce1 100644 --- a/Classes/AQGridView.m +++ b/Classes/AQGridView.m @@ -714,7 +714,7 @@ - (void) fixCellsFromAnimation NSMutableSet * removals = [[NSMutableSet alloc] init]; for ( UIView * view in self.subviews ) { - if ( [view isKindOfClass: [KBGridViewCell class]] == NO ) + if ( [view isKindOfClass: [AQGridViewCell class]] == NO ) continue; if ( [_visibleCells containsObject: view] == NO ) @@ -753,6 +753,22 @@ - (void) endUpdateAnimations return; } + NSUInteger expectedItemCount = [_updateInfo numberOfItemsAfterUpdates]; + NSUInteger actualItemCount = [_dataSource numberOfItemsInGridView: self]; + if ( expectedItemCount != actualItemCount ) + { + NSUInteger numAdded = [[_updateInfo sortedInsertItems] count]; + NSUInteger numDeleted = [[_updateInfo sortedDeleteItems] count]; + + //_reloadingSuspendedCount--; + _flags.isAnimatingUpdates = 0; + _flags.updating = 0; + [_updateInfo release]; + _updateInfo = nil; + + [NSException raise: NSInternalInconsistencyException format: @"Invalid number of items in AQGridView: Started with %u, added %u, deleted %u. Expected %u items after changes, but got %u", (unsigned)_gridData.numberOfItems, (unsigned)numAdded, (unsigned)numDeleted, (unsigned)expectedItemCount, (unsigned)actualItemCount]; + } + [_updateInfo cleanupUpdateItems]; [UIView beginAnimations: @"CellUpdates" context: nil]; diff --git a/Classes/AQGridViewUpdateInfo.h b/Classes/AQGridViewUpdateInfo.h index 81d44a1..61995d9 100644 --- a/Classes/AQGridViewUpdateInfo.h +++ b/Classes/AQGridViewUpdateInfo.h @@ -98,6 +98,7 @@ - (NSArray *) sortedReloadItems; - (AQGridViewData *) newGridViewData; +- (NSUInteger) numberOfItemsAfterUpdates; - (NSUInteger) newIndexForOldIndex: (NSUInteger) oldIndex; diff --git a/Classes/AQGridViewUpdateInfo.m b/Classes/AQGridViewUpdateInfo.m index cdf92eb..beb63e0 100644 --- a/Classes/AQGridViewUpdateInfo.m +++ b/Classes/AQGridViewUpdateInfo.m @@ -239,7 +239,7 @@ - (void) updateNewGridDataAndCreateMappingTables } - (void) cleanupUpdateItems -{ +{ // sort the lists in ascending order [_insertItems sortUsingSelector: @selector(inverseCompare:)]; [_deleteItems sortUsingSelector: @selector(inverseCompare:)]; @@ -349,6 +349,11 @@ - (AQGridViewData *) newGridViewData return ( [[_newGridData retain] autorelease] ); } +- (NSUInteger) numberOfItemsAfterUpdates +{ + return ( _newGridData.numberOfItems + [_insertItems count] - [_deleteItems count] ); +} + - (UIImageView *) _imageViewForView: (UIView *) view { UIGraphicsBeginImageContext(view.bounds.size); From 7626d30ac22a5c589162ebfbe589f2e0f4d6fccf Mon Sep 17 00:00:00 2001 From: Jim Dovey Date: Wed, 26 May 2010 15:13:42 -0400 Subject: [PATCH 09/10] Updated README. --- README.textile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.textile b/README.textile index ee3fbaf..abe6c32 100644 --- a/README.textile +++ b/README.textile @@ -1,8 +1,8 @@ h1=. AQGridView -h2=. Winner of the _Best Developer Tool/Helper_ award at iPadDevCamp 2010 in San Jose +h3=. Winner of the _Best Developer Tool/Helper_ award at iPadDevCamp 2010 in San Jose -p=. Version 1.0 -- 17 April 2010 +p=. Version 1.1 -- 26 May 2010 p=. By "Jim Dovey":mailto:jimdovey@mac.com
Originally written for the "Kobo iPad Application":http://itunes.apple.com/ca/app/ebooks-by-kobo-hd/id364742849?mt=8 @@ -64,7 +64,7 @@ h2. Future Directions * *Section support*. This will need a large amount of refactoring to support moves between sections, and will need a new way of keeping track of visible cell indices (it currently uses an NSRange). * *High-performance rendering*. If cells don't need to update their content after being drawn, each row could be composited into a single view for reduced load on the CoreAnimation renderer. This would need support in the grid view for displaying these composited rows, and would also need special support in KBGridViewCell to mark individual cells as needing dynamic updates, so they could be skipped when compositing and displayed normally on top of the composited row. KVO would be used to keep track of this. NB: This would also need special handling for the 'glow' selection style (or possibly the tracking cell would always be placed on screen, regardless of its dynamism requirements). -* *Content adjustments*. There are possibly still a couple of deeply-buried bugs in the cell movement code inside *KBGridViewUpdateInfo*. These are a pain to track down, and the code in that class could possibly use some cleanup. This is also something which whould need to change a lot for section support (it makes heavy use of *NSIndexSet* right now). +* *Content adjustments*. There are possibly still a couple of deeply-buried bugs in the cell movement code inside *KBGridViewUpdateInfo*. These are a pain to track down, and the code in that class could possibly use some cleanup. This is also something which would need to change a lot for section support (it makes heavy use of *NSIndexSet* right now). h2. Known Bugs From 2a86b778abe7f7f5249084f709453e53961c6fed Mon Sep 17 00:00:00 2001 From: catlan Date: Thu, 27 May 2010 03:42:05 +0800 Subject: [PATCH 10/10] add indexForCell --- Classes/AQGridView.h | 1 + Classes/AQGridView.m | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Classes/AQGridView.h b/Classes/AQGridView.h index 333fbc9..d279a70 100644 --- a/Classes/AQGridView.h +++ b/Classes/AQGridView.h @@ -172,6 +172,7 @@ extern NSString * const AQGridViewSelectionDidChangeNotification; - (CGRect) rectForItemAtIndex: (NSUInteger) index; - (AQGridViewCell *) cellForItemAtIndex: (NSUInteger) index; - (NSUInteger) indexForItemAtPoint: (CGPoint) point; +- (NSUInteger) indexForCell: (AQGridViewCell *) cell; - (AQGridViewCell *) cellForItemAtPoint: (CGPoint) point; - (NSArray *) visibleCells; diff --git a/Classes/AQGridView.m b/Classes/AQGridView.m index 19a1ce1..504bc89 100644 --- a/Classes/AQGridView.m +++ b/Classes/AQGridView.m @@ -615,6 +615,15 @@ - (NSUInteger) indexForItemAtPoint: (CGPoint) point return ( [_gridData itemIndexForPoint: point] ); } +- (NSUInteger) indexForCell: (AQGridViewCell *) cell +{ + NSUInteger index = [_visibleCells indexOfObject:cell]; + if (index == NSNotFound) + return NSNotFound; + + return _visibleIndices.location + index; +} + - (AQGridViewCell *) cellForItemAtPoint: (CGPoint) point { return ( [self cellForItemAtIndex: [_gridData itemIndexForPoint: point]] );