Permalink
Browse files

Optimizations using instruments and readme update

  • Loading branch information...
1 parent 4cf9e41 commit 087c37f4d66a65870b068d42fd0b731726dec329 @gmoledina committed Oct 24, 2011
Showing with 115 additions and 62 deletions.
  1. +8 −1 GMGridView/API/GMGridView.h
  2. +102 −58 GMGridView/API/GMGridView.m
  3. +1 −1 GMGridView/API/GMGridViewCell.m
  4. +4 −2 README
View
9 GMGridView/API/GMGridView.h
@@ -72,13 +72,16 @@ typedef enum
@protocol GMGridViewDataSource
+// Populating subview items
- (NSInteger)numberOfItemsInGMGridView:(GMGridView *)gridView;
- (NSInteger)widthForItemsInGMGridView:(GMGridView *)gridView;
- (NSInteger)heightForItemsInGMGridView:(GMGridView *)gridView;
- (UIView *)GMGridView:(GMGridView *)gridView viewForItemAtIndex:(NSInteger)index;
+
+// Item moved - right place to update the data structure
- (void)GMGridView:(GMGridView *)gridView itemAtIndex:(NSInteger)oldIndex movedToIndex:(NSInteger)newIndex;
-//@optional
+// Fullsize
- (CGSize)GMGridView:(GMGridView *)gridView fullSizeForView:(UIView *)view;
- (UIView *)GMGridView:(GMGridView *)gridView fullSizeViewForView:(UIView *)view;
@@ -91,8 +94,11 @@ typedef enum
@protocol GMGridViewSortingDelegate
+// Sorting started/ended - indexes are not specified on purpose (not the right place to update data structure)
- (void)GMGridView:(GMGridView *)gridView didStartMovingView:(UIView *)view;
- (void)GMGridView:(GMGridView *)gridView didEndMovingView:(UIView *)view;
+
+// Enable/Disable the shaking behavior of an item being moved
- (BOOL)GMGridView:(GMGridView *)gridView shouldAllowShakingBehaviorWhenMovingView:(UIView *)view atIndex:(NSInteger)index;
@end
@@ -103,6 +109,7 @@ typedef enum
@protocol GMGridViewTransformationDelegate
+// Transformation (pinch, drag, rotate) of the an item
- (void)GMGridView:(GMGridView *)gridView didStartTransformingView:(UIView *)view;
- (void)GMGridView:(GMGridView *)gridView didEnterFullSizeForView:(UIView *)view;
- (void)GMGridView:(GMGridView *)gridView didEndTransformingView:(UIView *)view;
View
160 GMGridView/API/GMGridView.m
@@ -57,6 +57,7 @@ @interface GMGridView () <UIGestureRecognizerDelegate>
// General vars
NSInteger _numberOfItemsPerRow;
+ NSInteger _numberOfRowsInPage;
NSInteger _numberTotalItems;
CGSize _itemSize;
@@ -73,6 +74,11 @@ @interface GMGridView () <UIGestureRecognizerDelegate>
BOOL _inFullSizeMode;
}
+
+@property (nonatomic, assign) BOOL cacheStatusIsValid;
+@property (nonatomic, strong) NSArray *itemSubviewsCache;
+
+
// Gestures
- (void)sortingPanGestureUpdated:(UIPanGestureRecognizer *)panGesture;
- (void)sortingLongPressGestureUpdated:(UILongPressGestureRecognizer *)longPressGesture;
@@ -95,13 +101,14 @@ - (BOOL)isInTransformingState;
- (void)exitFullSizePinchGestureUpdated:(UIPinchGestureRecognizer *)pinchGesture;
// Helpers & more
-- (CGSize)relayoutItems;
+- (void)relayoutItems;
- (CGPoint)originForItemAtPosition:(NSInteger)position;
- (NSInteger)itemPositionFromLocation:(CGPoint)location;
- (NSArray *)itemSubviews;
- (GMGridViewCell *)itemSubViewForPosition:(NSInteger)position;
- (GMGridViewCell *)createItemSubViewForPosition:(NSInteger)position;
- (NSInteger)positionForItemSubview:(GMGridViewCell *)view;
+- (CGSize)computeContentSize;
@end
@@ -120,6 +127,9 @@ @implementation GMGridView
@synthesize minimumPressDuration;
@synthesize centerGrid;
+@synthesize cacheStatusIsValid;
+@synthesize itemSubviewsCache;
+
//////////////////////////////////////////////////////////////
#pragma mark Constructors and destructor
//////////////////////////////////////////////////////////////
@@ -193,7 +203,30 @@ - (void)layoutSubviews
{
[super layoutSubviews];
- _scrollView.contentSize = [self relayoutItems];
+ NSUInteger itemsPerRow = 1;
+
+ while ((itemsPerRow+1) * (_itemSize.width + self.itemPadding) + self.itemPadding < self.bounds.size.width)
+ {
+ itemsPerRow++;
+ }
+
+ _numberOfItemsPerRow = itemsPerRow;
+ _numberOfRowsInPage = ceil(_numberTotalItems / (1.0 * _numberOfItemsPerRow));
+
+ if (self.centerGrid)
+ {
+ int extraSpace = self.bounds.size.width - (_numberOfItemsPerRow * (_itemSize.width + self.itemPadding)) - self.itemPadding;
+ extraSpace /= 2;
+ _scrollView.contentInset = UIEdgeInsetsMake(0, extraSpace, 0, extraSpace);
+ }
+ else
+ {
+ _scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
+ }
+
+ [self relayoutItems];
+
+ _scrollView.contentSize = [self computeContentSize];
[_scrollView flashScrollIndicators];
}
@@ -672,11 +705,7 @@ - (void)sortingMoveDidStartAtPoint:(CGPoint)point
[_scrollView bringSubviewToFront:item];
_sortMovingItem = item;
-
- CGRect frameInMainView = CGRectMake(_sortMovingItem.frame.origin.x - _scrollView.contentOffset.x,
- _sortMovingItem.frame.origin.y - _scrollView.contentOffset.y,
- _sortMovingItem.frame.size.width,
- _sortMovingItem.frame.size.height);
+ CGRect frameInMainView = [_scrollView convertRect:_sortMovingItem.frame toView:self];
[_sortMovingItem removeFromSuperview];
_sortMovingItem.frame = frameInMainView;
@@ -698,19 +727,12 @@ - (void)sortingMoveDidStopAtPoint:(CGPoint)point
_sortMovingItem.tag = _sortFuturePosition + GMGV_TAG_OFFSET;
-
- CGPoint position = [self originForItemAtPosition:_sortFuturePosition];
- CGRect frameInScroll = CGRectMake(position.x,
- position.y,
- _sortMovingItem.frame.size.width,
- _sortMovingItem.frame.size.height);
+ CGRect frameInScroll = [self convertRect:_sortMovingItem.frame toView:_scrollView];
[_sortMovingItem removeFromSuperview];
_sortMovingItem.frame = frameInScroll;
[_scrollView addSubview:_sortMovingItem];
-
-
[self updateIndexOfItem:_sortMovingItem toIndex:_sortFuturePosition];
[UIView animateWithDuration:0.2
@@ -755,7 +777,7 @@ - (void)sortingMoveDidContinueToPoint:(CGPoint)point
{
for (UIView *v in [self itemSubviews])
{
- if (v != _sortMovingItem && (v.tag == tag || (v.tag < tag && v.tag >= _sortFuturePosition + GMGV_TAG_OFFSET)))
+ if ((v.tag == tag || (v.tag < tag && v.tag >= _sortFuturePosition + GMGV_TAG_OFFSET)) && v != _sortMovingItem )
{
v.tag = v.tag - 1;
[_scrollView sendSubviewToBack:v];
@@ -766,30 +788,41 @@ - (void)sortingMoveDidContinueToPoint:(CGPoint)point
{
for (UIView *v in [self itemSubviews])
{
- if (v != _sortMovingItem && (v.tag == tag || (v.tag > tag && v.tag <= _sortFuturePosition + GMGV_TAG_OFFSET)))
+ if ((v.tag == tag || (v.tag > tag && v.tag <= _sortFuturePosition + GMGV_TAG_OFFSET)) && v != _sortMovingItem)
{
v.tag = v.tag + 1;
[_scrollView sendSubviewToBack:v];
}
}
}
+
+ [self relayoutItems];
+
break;
}
case GMGridViewStyleSwap:
default:
{
UIView *v = [_scrollView viewWithTag:tag];
v.tag = _sortFuturePosition + GMGV_TAG_OFFSET;
- [_scrollView sendSubviewToBack:v];
+ CGPoint origin = [self originForItemAtPosition:_sortFuturePosition];
+
+ [UIView animateWithDuration:0.3
+ delay:0
+ options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ v.frame = CGRectMake(origin.x, origin.y, _itemSize.width, _itemSize.height);
+ }
+ completion:nil
+ ];
+
[self updateIndexOfItem:v toIndex:v.tag - GMGV_TAG_OFFSET];
break;
}
}
}
_sortFuturePosition = position;
-
- [self setNeedsLayout];
}
}
@@ -804,6 +837,8 @@ - (void)reloadData
[(UIView *)obj removeFromSuperview];
}];
+ self.cacheStatusIsValid = NO;
+
NSUInteger numberItems = [self.dataSource numberOfItemsInGMGridView:self];
NSUInteger width = [self.dataSource widthForItemsInGMGridView:self];
NSUInteger height = [self.dataSource heightForItemsInGMGridView:self];
@@ -818,6 +853,8 @@ - (void)reloadData
[_scrollView addSubview:cell];
}
+ self.cacheStatusIsValid = NO;
+
[self setNeedsLayout];
}
@@ -831,6 +868,7 @@ - (void)reloadObjectAtIndex:(NSInteger)index
cell.frame = currentView.frame;
cell.alpha = 0;
[_scrollView addSubview:cell];
+ self.cacheStatusIsValid = NO;
currentView.tag = GMGV_TAG_OFFSET - 1;
@@ -866,10 +904,12 @@ - (void)insertObjectAtIndex:(NSInteger)index
_numberTotalItems++;
[_scrollView addSubview:cell];
+ self.cacheStatusIsValid = NO;
- // instead of calling [self setNeedsLayout] so we can animate to the item even if it's at the bottom
- _scrollView.contentSize = [self relayoutItems];
+ _scrollView.contentSize = [self computeContentSize];
[_scrollView scrollRectToVisible:cell.frame animated:YES];
+
+ [self setNeedsLayout];
}
- (void)removeObjectAtIndex:(NSInteger)index
@@ -895,6 +935,7 @@ - (void)removeObjectAtIndex:(NSInteger)index
completion:^(BOOL finished){
[cell removeFromSuperview];
_numberTotalItems--;
+ self.cacheStatusIsValid = NO;
[self setNeedsLayout];
}
];
@@ -914,6 +955,10 @@ - (void)swapObjectAtIndex:(NSInteger)index1 withObjectAtIndex:(NSInteger)index2
view1.tag = view2.tag;
view2.tag = tempTag;
+ CGRect tempFrame = view1.frame;
+ view1.frame = view2.frame;
+ view2.frame = tempFrame;
+
CGRect visibleRect = CGRectMake(_scrollView.contentOffset.x,
_scrollView.contentOffset.y,
_scrollView.contentSize.width,
@@ -927,8 +972,6 @@ - (void)swapObjectAtIndex:(NSInteger)index1 withObjectAtIndex:(NSInteger)index2
{
[_scrollView scrollRectToVisible:view2.frame animated:YES];
}
-
- [self setNeedsLayout];
}
@@ -948,20 +991,34 @@ - (GMGridViewCell *)createItemSubViewForPosition:(NSInteger)position
}
- (NSArray *)itemSubviews
-{
- NSMutableArray *itemSubViews = [[NSMutableArray alloc] initWithCapacity:_numberTotalItems];
+{
+ NSArray *subviews = nil;
- for (UIView * v in [_scrollView subviews])
+ if (self.cacheStatusIsValid)
+ {
+ subviews = [self.itemSubviewsCache copy];
+ }
+ else
{
- if (v.tag >= GMGV_TAG_OFFSET && [v isKindOfClass:[GMGridViewCell class]])
+ NSMutableArray *itemSubViews = [[NSMutableArray alloc] initWithCapacity:_numberTotalItems];
+
+ for (UIView * v in [_scrollView subviews])
{
- [itemSubViews addObject:v];
+ if (v.tag >= GMGV_TAG_OFFSET && [v isKindOfClass:[GMGridViewCell class]])
+ {
+ [itemSubViews addObject:v];
+ }
}
- }
- return itemSubViews;
+ subviews = itemSubViews;
+ self.itemSubviewsCache = [subviews copy];
+ self.cacheStatusIsValid = YES;
+ }
+
+ return subviews;
}
+// TODO: validate if this method is used ( = viewWithTag)
- (GMGridViewCell *)itemSubViewForPosition:(NSInteger)position
{
GMGridViewCell *view = nil;
@@ -1041,30 +1098,15 @@ - (NSInteger)itemPositionFromLocation:(CGPoint)location
return position;
}
-- (CGSize)relayoutItems
+- (CGSize)computeContentSize
{
- NSUInteger itemsPerRow = 1;
-
- while ((itemsPerRow+1) * (_itemSize.width + self.itemPadding) + self.itemPadding < self.bounds.size.width)
- {
- itemsPerRow++;
- }
-
- _numberOfItemsPerRow = itemsPerRow;
- int numberOfRowsInPage = ceil(_numberTotalItems / (1.0 * _numberOfItemsPerRow));
-
- if (self.centerGrid)
- {
- int extraSpace = self.bounds.size.width - (itemsPerRow * (_itemSize.width + self.itemPadding)) - self.itemPadding;
- extraSpace /= 2;
- _scrollView.contentInset = UIEdgeInsetsMake(0, extraSpace, 0, extraSpace);
- }
- else
- {
- _scrollView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
- }
-
+ return CGSizeMake(self.bounds.size.width - _scrollView.contentInset.left - _scrollView.contentInset.right,
+ ceil(_numberOfRowsInPage * (_itemSize.height + self.itemPadding) + self.itemPadding));
+}
+
+- (void)relayoutItems
+{
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionAllowAnimatedContent | UIViewAnimationOptionBeginFromCurrentState
@@ -1076,17 +1118,19 @@ - (CGSize)relayoutItems
{
NSInteger index = view.tag - GMGV_TAG_OFFSET;
CGPoint origin = [self originForItemAtPosition:index];
+ CGRect newFrame = CGRectMake(origin.x, origin.y, _itemSize.width, _itemSize.height);
- view.frame = CGRectMake(origin.x, origin.y, _itemSize.width, _itemSize.height);
+ if (!CGRectEqualToRect(newFrame, view.frame))
+ {
+ view.frame = newFrame;
+ }
}
}
}
completion:^(BOOL finished) {
- }];
-
- return CGSizeMake(self.bounds.size.width - _scrollView.contentInset.left - _scrollView.contentInset.right,
- ceil(numberOfRowsInPage * (_itemSize.height + self.itemPadding) + self.itemPadding));
+ }
+ ];
}
View
2 GMGridView/API/GMGridViewCell.m
@@ -65,7 +65,7 @@ @implementation GMGridViewCell
- (id)initWithFrame:(CGRect)frame
{
- if ([self initContentView:nil])
+ if ((self = [self initContentView:nil]))
{
self.frame = frame;
}
View
6 README
@@ -7,7 +7,8 @@ This view is inspired by the UITableView and uses a datasource and delegates in
Requirements:
- iOS 5 (to access the UIScrollView gestureRecognizers)
-- ARC
+- ARC (Automatic Reference Counting)
+- Frameworks Foundation, UIKit, CoreGraphics and QuartzCore
Features - General:
- Works on both the iPhone and iPad
@@ -22,9 +23,10 @@ Features - Sorting:
- Only one UIPanGestureRecognizer and one UILongTouchGestureRecognizer used to track ALL views
Features - Fullsize:
-- Pinch, rotate, drag views using 2 fingers
+- Pinch, rotate and drag views using 2 fingers
- Switch to fullsize mode on the view at the end of these gestures if the view scaled enough
- Provide a different fullsize view (detailed view) for the view via the delegate
- Every view doesnt have it's own gesture recognizers, the main view handles a set of gestures for ALL views
+
Latest code can be found on GitHub: https://github.com/gmoledina/GMGridView

0 comments on commit 087c37f

Please sign in to comment.