Permalink
Browse files

Added horizontal paged strategy

  • Loading branch information...
1 parent 4161cdd commit 9ee51dde177f9890727732dc65339ab3531ab4be @gmoledina committed Dec 11, 2011
View
2 GMGridView/API/GMGridView.h
@@ -82,7 +82,7 @@ typedef enum
- (void)removeObjectAtIndex:(NSInteger)index;
- (void)reloadObjectAtIndex:(NSInteger)index;
- (void)swapObjectAtIndex:(NSInteger)index1 withObjectAtIndex:(NSInteger)index2;
-- (void)scrollToObjectAtIndex:(NSInteger)index;
+- (void)scrollToObjectAtIndex:(NSInteger)index animated:(BOOL)animated;
@end
View
62 GMGridView/API/GMGridView.m
@@ -273,6 +273,8 @@ - (void)setMainSuperView:(UIView *)mainSuperView
- (void)setLayoutStrategy:(id<GMGridViewLayoutStrategy>)layoutStrategy
{
_layoutStrategy = layoutStrategy;
+
+ _scrollView.pagingEnabled = [[self.layoutStrategy class] requiresEnablingPaging];
[self setNeedsLayout];
}
@@ -1073,8 +1075,7 @@ - (void)recomputeSize
CGSize contentSize = [self.layoutStrategy contentSize];
- _minPossibleContentOffset = CGPointMake(-1 * (_scrollView.contentInset.left),
- -1 * (_scrollView.contentInset.top));
+ _minPossibleContentOffset = CGPointMake(0, 0);
_maxPossibleContentOffset = CGPointMake(contentSize.width - _scrollView.bounds.size.width + _scrollView.contentInset.right,
contentSize.height - _scrollView.bounds.size.height + _scrollView.contentInset.bottom);
@@ -1270,12 +1271,11 @@ - (void)reloadObjectAtIndex:(NSInteger)index
currentView.tag = kTagOffset - 1;
- // Better performance animating ourselves instead of using animated:YES in scrollRectToVisible
[UIView animateWithDuration:kDefaultAnimationDuration
delay:0
options:kDefaultAnimationOptions
animations:^{
- [_scrollView scrollRectToVisible:cell.frame animated:NO];
+ [self scrollToObjectAtIndex:index animated:NO];
currentView.alpha = 0;
cell.alpha = 1;
}
@@ -1288,18 +1288,40 @@ - (void)reloadObjectAtIndex:(NSInteger)index
[self setSubviewsCacheAsInvalid];
}
-- (void)scrollToObjectAtIndex:(NSInteger)index
+- (void)scrollToObjectAtIndex:(NSInteger)index animated:(BOOL)animated
{
- NSAssert((index >= 0 && index < _numberTotalItems), @"Invalid index");
-
+ index = MAX(0, index);
+ index = MIN(index, _numberTotalItems);
+
CGPoint origin = [self.layoutStrategy originForItemAtPosition:index];
+ CGRect scrollToRect = CGRectMake(origin.x, origin.y, _itemSize.width, _itemSize.height);
+
+ if (_scrollView.pagingEnabled)
+ {
+ CGPoint originScroll = CGPointZero;
+
+ CGSize pageSize = CGSizeMake(_scrollView.bounds.size.width - _scrollView.contentInset.left - _scrollView.contentInset.right,
+ _scrollView.bounds.size.height - _scrollView.contentInset.top - _scrollView.contentInset.bottom);
+
+ while (originScroll.x + pageSize.width < origin.x)
+ {
+ originScroll.x += pageSize.width;
+ }
+
+ while (originScroll.y + pageSize.height < origin.y)
+ {
+ originScroll.y += pageSize.height;
+ }
+
+ scrollToRect = CGRectMake(originScroll.x, originScroll.y, pageSize.width, pageSize.height);
+ }
// Better performance animating ourselves instead of using animated:YES in scrollRectToVisible
- [UIView animateWithDuration:kDefaultAnimationDuration
+ [UIView animateWithDuration:animated ? kDefaultAnimationDuration : 0
delay:0
options:kDefaultAnimationOptions
animations:^{
- [_scrollView scrollRectToVisible:CGRectMake(origin.x, origin.y, _itemSize.width, _itemSize.height) animated:NO];
+ [_scrollView scrollRectToVisible:scrollToRect animated:NO];
}
completion:^(BOOL finished){
}
@@ -1311,12 +1333,9 @@ - (void)insertObjectAtIndex:(NSInteger)index
NSAssert((index >= 0 && index <= _numberTotalItems), @"Invalid index specified");
GMGridViewCell *cell = nil;
- BOOL isInsertedObjectVisible = NO;
if (index >= self.firstPositionLoaded && index <= self.lastPositionLoaded)
- {
- isInsertedObjectVisible = YES;
-
+ {
cell = [self newItemSubViewForPosition:index];
for (int i = index; i < _numberTotalItems; i++)
@@ -1331,23 +1350,17 @@ - (void)insertObjectAtIndex:(NSInteger)index
_numberTotalItems++;
[self recomputeSize];
- // We cant use cell.frame as it might not be loaded yet
- CGPoint newObjectOrigin = [self.layoutStrategy originForItemAtPosition:index];
-
- // Better performance animating ourselves instead of using animated:YES in scrollRectToVisible
[UIView animateWithDuration:kDefaultAnimationDuration
delay:0
options:kDefaultAnimationOptions
animations:^{
- [_scrollView scrollRectToVisible:CGRectMake(newObjectOrigin.x, newObjectOrigin.y, _itemSize.width, _itemSize.height) animated:NO];
+ [self scrollToObjectAtIndex:index animated:NO];
}
completion:^(BOOL finished){
[self setNeedsLayout];
}
];
-
-
[self setSubviewsCacheAsInvalid];
}
@@ -1366,17 +1379,14 @@ - (void)removeObjectAtIndex:(NSInteger)index
cell.tag = kTagOffset - 1;
_numberTotalItems--;
- CGPoint origin = [self.layoutStrategy originForItemAtPosition:index];
-
- // Better performance animating ourselves instead of using animated:YES in scrollRectToVisible
[UIView animateWithDuration:kDefaultAnimationDuration
delay:0
options:kDefaultAnimationOptions
animations:^{
cell.contentView.alpha = 0.3;
cell.alpha = 0;
- [_scrollView scrollRectToVisible:CGRectMake(origin.x, origin.y, _itemSize.width, _itemSize.height) animated:NO];
+ [self scrollToObjectAtIndex:index animated:NO];
[self recomputeSize];
}
@@ -1424,11 +1434,11 @@ - (void)swapObjectAtIndex:(NSInteger)index1 withObjectAtIndex:(NSInteger)index2
animations:^{
if (!CGRectIntersectsRect(view2.frame, visibleRect))
{
- [_scrollView scrollRectToVisible:view1.frame animated:NO];
+ [self scrollToObjectAtIndex:index1 animated:NO];
}
else if (!CGRectIntersectsRect(view1.frame, visibleRect))
{
- [_scrollView scrollRectToVisible:view2.frame animated:NO];
+ [self scrollToObjectAtIndex:index2 animated:NO];
}
}
completion:^(BOOL finished){
View
23 GMGridView/API/GMGridViewLayoutStrategies.h
@@ -35,7 +35,8 @@
typedef enum {
GMGridViewLayoutVertical = 0,
- GMGridViewLayoutHorizontal
+ GMGridViewLayoutHorizontal,
+ GMGridViewLayoutHorizontalPaged
} GMGridViewLayoutStrategyType;
@@ -57,6 +58,8 @@ typedef enum {
@protocol GMGridViewLayoutStrategy <NSObject>
++ (BOOL)requiresEnablingPaging;
+
- (GMGridViewLayoutStrategyType)type;
// Setup
@@ -149,4 +152,22 @@ typedef enum {
@end
+//////////////////////////////////////////////////////////////
+#pragma mark - Horizontal strategy
+//////////////////////////////////////////////////////////////
+
+@interface GMGridViewLayoutHorizontalPagedStrategy : GMGridViewLayoutHorizontalStrategy <GMGridViewLayoutStrategy>
+{
+ @protected
+ NSInteger _numberOfItemsPerRow;
+ NSInteger _numberOfItemsPerPage;
+ NSInteger _numberOfPages;
+}
+
+@property (nonatomic, readonly) NSInteger numberOfItemsPerRow;
+@property (nonatomic, readonly) NSInteger numberOfItemsPerPage;
+@property (nonatomic, readonly) NSInteger numberOfPages;
+
+@end
+
View
182 GMGridView/API/GMGridViewLayoutStrategies.m
@@ -43,6 +43,9 @@ @implementation GMGridViewLayoutStrategyFactory
case GMGridViewLayoutVertical:
strategy = [[GMGridViewLayoutVerticalStrategy alloc] init];
break;
+ case GMGridViewLayoutHorizontalPaged:
+ strategy = [[GMGridViewLayoutHorizontalPagedStrategy alloc] init];
+ break;
case GMGridViewLayoutHorizontal:
default:
strategy = [[GMGridViewLayoutHorizontalStrategy alloc] init];
@@ -107,8 +110,8 @@ - (void)setEdgeAndContentSizeFromAbsoluteContentSize:(CGSize)actualContentSize
_edgeInsets = self.minEdgeInsets;
}
-
- _contentSize = actualContentSize;
+ _contentSize = CGSizeMake(actualContentSize.width + self.edgeInsets.left + self.edgeInsets.right,
+ actualContentSize.height + self.edgeInsets.top + self.edgeInsets.bottom);
}
@end
@@ -124,6 +127,11 @@ @implementation GMGridViewLayoutVerticalStrategy
@synthesize numberOfItemsPerRow = _numberOfItemsPerRow;
++ (BOOL)requiresEnablingPaging
+{
+ return NO;
+}
+
- (id)init
{
if ((self = [super init]))
@@ -144,7 +152,6 @@ - (void)rebaseWithItemCount:(NSInteger)count insideOfBounds:(CGRect)bounds
bounds.size.width - self.minEdgeInsets.right - self.minEdgeInsets.left,
bounds.size.height - self.minEdgeInsets.top - self.minEdgeInsets.bottom);
-
_numberOfItemsPerRow = 1;
while ((self.numberOfItemsPerRow + 1) * (self.itemSize.width + self.itemSpacing) - self.itemSpacing < actualBounds.size.width)
@@ -242,6 +249,11 @@ @implementation GMGridViewLayoutHorizontalStrategy
@synthesize numberOfItemsPerColumn = _numberOfItemsPerColumn;
++ (BOOL)requiresEnablingPaging
+{
+ return NO;
+}
+
- (id)init
{
if ((self = [super init]))
@@ -254,8 +266,8 @@ - (id)init
- (void)rebaseWithItemCount:(NSInteger)count insideOfBounds:(CGRect)bounds
{
- _itemCount = count;
- _gridBounds = bounds;
+ _itemCount = count;
+ _gridBounds = bounds;
CGRect actualBounds = CGRectMake(0,
0,
@@ -350,10 +362,170 @@ - (NSRange)rangeOfPositionsInBoundsFromOffset:(CGPoint)offset
+//////////////////////////////////////////////////////////////
+#pragma mark -
+#pragma mark - HorizontalPaged strategy implementation
+//////////////////////////////////////////////////////////////
+
+@implementation GMGridViewLayoutHorizontalPagedStrategy
+
+@synthesize numberOfItemsPerPage = _numberOfItemsPerPage;
+@synthesize numberOfItemsPerRow = _numberOfItemsPerRow;
+@synthesize numberOfPages = _numberOfPages;
+
++ (BOOL)requiresEnablingPaging
+{
+ return YES;
+}
+
+- (id)init
+{
+ if ((self = [super init]))
+ {
+ _type = GMGridViewLayoutHorizontalPaged;
+ }
+
+ return self;
+}
+
+- (void)rebaseWithItemCount:(NSInteger)count insideOfBounds:(CGRect)bounds
+{
+ [super rebaseWithItemCount:count insideOfBounds:bounds];
+
+ CGRect actualBounds = CGRectMake(0,
+ 0,
+ bounds.size.width - self.minEdgeInsets.right - self.minEdgeInsets.left,
+ bounds.size.height - self.minEdgeInsets.top - self.minEdgeInsets.bottom);
+
+ _numberOfItemsPerRow = 1;
+
+ NSInteger gridContentMaxWidth = self.gridBounds.size.width - self.minEdgeInsets.right - self.minEdgeInsets.left;
+
+ while ((self.numberOfItemsPerRow + 1) * (self.itemSize.width + self.itemSpacing) - self.itemSpacing <= gridContentMaxWidth)
+ {
+ _numberOfItemsPerRow++;
+ }
+
+ _numberOfItemsPerPage = _numberOfItemsPerRow * _numberOfItemsPerColumn;
+ _numberOfPages = ceil(self.itemCount * 1.0 / self.numberOfItemsPerPage);
+
+ CGSize onePageSize = CGSizeMake(self.numberOfItemsPerRow * (self.itemSize.width + self.itemSpacing) - self.itemSpacing,
+ self.numberOfItemsPerColumn * (self.itemSize.height + self.itemSpacing) - self.itemSpacing);
+
+ if (self.centeredGrid)
+ {
+ NSInteger widthSpace, heightSpace;
+ NSInteger top, left, bottom, right;
+
+ widthSpace = floor((self.gridBounds.size.width - onePageSize.width) / 2.0);
+ heightSpace = floor((self.gridBounds.size.height - onePageSize.height) / 2.0);
+
+ left = MAX(widthSpace, self.minEdgeInsets.left);
+ right = MAX(widthSpace, self.minEdgeInsets.right);
+ top = MAX(heightSpace, self.minEdgeInsets.top);
+ bottom = MAX(heightSpace, self.minEdgeInsets.bottom);
+
+ _edgeInsets = UIEdgeInsetsMake(top, left, bottom, right);
+ }
+ else
+ {
+ _edgeInsets = self.minEdgeInsets;
+ }
+
+ _contentSize = CGSizeMake((onePageSize.width + self.edgeInsets.left + self.edgeInsets.right) * self.numberOfPages,
+ onePageSize.height + self.edgeInsets.top + self.edgeInsets.bottom);
+
+ _contentBounds = CGRectMake(actualBounds.origin.x + _edgeInsets.left,
+ actualBounds.origin.y + _edgeInsets.top,
+ actualBounds.size.width -_edgeInsets.left - _edgeInsets.right,
+ actualBounds.size.height - _edgeInsets.top - _edgeInsets.bottom);
+
+
+}
+
+- (NSInteger)pageForItemAtIndex:(NSInteger)index
+{
+ return MAX(0, floor(index * 1.0 / self.numberOfItemsPerPage * 1.0));
+}
+- (CGPoint)originForItemAtColumn:(NSInteger)column row:(NSInteger)row page:(NSInteger)page
+{
+ CGPoint offset = CGPointMake(page * self.gridBounds.size.width,
+ 0);
+
+ CGFloat x = column * (self.itemSize.width + self.itemSpacing) + self.edgeInsets.left;
+ CGFloat y = row * (self.itemSize.height + self.itemSpacing) + self.edgeInsets.top;
+
+ return CGPointMake(x + offset.x,
+ y + offset.y);
+}
+- (CGPoint)originForItemAtPosition:(NSInteger)position
+{
+ NSUInteger page = [self pageForItemAtIndex:position];
+
+ position %= self.numberOfItemsPerPage;
+
+ NSUInteger column = position % self.numberOfItemsPerRow;
+ NSUInteger row = floor(position / self.numberOfItemsPerRow);
+
+ CGPoint origin = [self originForItemAtColumn:column row:row page:page];
+
+ return origin;
+
+}
+- (NSInteger)itemPositionFromLocation:(CGPoint)location
+{
+ CGFloat page = 0;
+ while ((page + 1) * self.gridBounds.size.width < location.x)
+ {
+ page++;
+ }
+
+ CGPoint originForFirstItemInPage = [self originForItemAtColumn:0 row:0 page:page];
+
+ CGPoint relativeLocation = CGPointMake(location.x - originForFirstItemInPage.x,
+ location.y - originForFirstItemInPage.y);
+ int col = (int) (relativeLocation.x / (self.itemSize.width + self.itemSpacing));
+ int row = (int) (relativeLocation.y / (self.itemSize.height + self.itemSpacing));
+
+ int position = col + row * self.numberOfItemsPerRow + (page * self.numberOfItemsPerPage);
+
+ if (position >= [self itemCount] || position < 0)
+ {
+ position = GMGV_INVALID_POSITION;
+ }
+ else
+ {
+ CGPoint itemOrigin = [self originForItemAtPosition:position];
+ CGRect itemFrame = CGRectMake(itemOrigin.x,
+ itemOrigin.y,
+ self.itemSize.width,
+ self.itemSize.height);
+
+ if (!CGRectContainsPoint(itemFrame, location))
+ {
+ position = GMGV_INVALID_POSITION;
+ }
+ }
+
+ return position;
+}
+- (NSRange)rangeOfPositionsInBoundsFromOffset:(CGPoint)offset
+{
+ CGPoint contentOffset = CGPointMake(MAX(0, offset.x),
+ MAX(0, offset.y));
+
+ NSInteger page = floor(contentOffset.x / self.gridBounds.size.width);
+
+ NSInteger firstPosition = MAX(0, (page - 1) * self.numberOfItemsPerPage);
+ NSInteger lastPosition = MIN(firstPosition + 3 * self.numberOfItemsPerPage, self.itemCount);
+
+ return NSMakeRange(firstPosition, (lastPosition - firstPosition));
+}
+@end
View
116 GMGridView/OptionsViewController.m
@@ -52,14 +52,13 @@
OptionDebugCount
} OptionsTypeDebug;
-@interface OptionsViewController () <UITableViewDelegate, UITableViewDataSource>
+@interface OptionsViewController () <UITableViewDelegate, UITableViewDataSource, UIPickerViewDelegate, UIPickerViewDataSource>
{
__weak UITableView *_tableView;
}
- (void)editingSwitchChanged:(UISwitch *)control;
- (void)sortStyleSegmentedControlChanged:(UISegmentedControl *)control;
-- (void)layoutStrategySegmentedControlChanged:(UISegmentedControl *)control;
- (void)layoutCenterSwitchChanged:(UISwitch *)control;
- (void)layoutSpacingSliderChanged:(UISlider *)control;
- (void)layoutInsetsSliderChanged:(UISlider *)control;
@@ -131,6 +130,23 @@ - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSIntege
return 35;
}
+- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ CGFloat height = 45;
+
+ if ([indexPath section] == OptionSectionLayout)
+ {
+ switch ([indexPath row])
+ {
+ case OptionTypeLayoutStrategy:
+ height = 160;
+ break;
+ }
+ }
+
+ return height;
+}
+
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return OptionSectionsCount;
@@ -194,7 +210,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
}
-
if ([indexPath section] == OptionSectionGeneral)
{
switch ([indexPath row])
@@ -217,14 +232,31 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
{
case OptionTypeLayoutStrategy:
{
- cell.detailTextLabel.text = @"Strategy";
+ cell.detailTextLabel.text = @"";
- UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:@"Vertical", @"Horizontal", nil]];
- segmentedControl.frame = CGRectMake(0, 0, 200, 30);
- [segmentedControl addTarget:self action:@selector(layoutStrategySegmentedControlChanged:) forControlEvents:UIControlEventValueChanged];
- segmentedControl.selectedSegmentIndex = [self.gridView.layoutStrategy type] == GMGridViewLayoutVertical ? 0 : 1;
+ UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:cell.contentView.bounds];
+ pickerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+ pickerView.showsSelectionIndicator = YES;
+ pickerView.delegate = self;
+ pickerView.dataSource = self;
+
+ switch ([self.gridView.layoutStrategy type])
+ {
+ case GMGridViewLayoutHorizontalPaged:
+ [pickerView selectRow:2 inComponent:0 animated:YES];
+ break;
+ case GMGridViewLayoutHorizontal:
+ [pickerView selectRow:1 inComponent:0 animated:YES];
+ break;
+ case GMGridViewLayoutVertical:
+ default:
+ [pickerView selectRow:0 inComponent:0 animated:YES];
+ break;
+ }
+
+ cell.contentView.clipsToBounds = YES;
+ [cell.contentView addSubview:pickerView];
- cell.accessoryView = segmentedControl;
break;
}
case OptionsTypeLayoutCenter:
@@ -334,39 +366,79 @@ - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)
//////////////////////////////////////////////////////////////
-#pragma mark Control callbacks
+#pragma mark UIPickerView delegate and datasource
//////////////////////////////////////////////////////////////
-- (void)editingSwitchChanged:(UISwitch *)control
-{
- self.gridView.editing = control.on;
- control.on = self.gridView.isEditing;
-}
-- (void)sortStyleSegmentedControlChanged:(UISegmentedControl *)control
+- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
- switch (control.selectedSegmentIndex)
+ switch (row)
{
case 1:
- self.gridView.style = GMGridViewStylePush;
+ self.gridView.layoutStrategy = [GMGridViewLayoutStrategyFactory strategyFromType:GMGridViewLayoutHorizontal];
+ break;
+ case 2:
+ self.gridView.layoutStrategy = [GMGridViewLayoutStrategyFactory strategyFromType:GMGridViewLayoutHorizontalPaged];
break;
case 0:
default:
- self.gridView.style = GMGridViewStyleSwap;
+ self.gridView.layoutStrategy = [GMGridViewLayoutStrategyFactory strategyFromType:GMGridViewLayoutVertical];
break;
}
}
-- (void)layoutStrategySegmentedControlChanged:(UISegmentedControl *)control
+- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
+{
+ return 1;
+}
+
+- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
+{
+ return 3;
+}
+
+- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
+{
+ NSString *title = nil;
+
+ switch (row) {
+ case 0:
+ title = @"Vertical strategy";
+ break;
+ case 1:
+ title = @"Horizontal strategy";
+ break;
+ case 2:
+ title = @"Horizontal paged strategy";
+ break;
+ default:
+ title = @"Unknown";
+ break;
+ }
+
+ return title;
+}
+
+//////////////////////////////////////////////////////////////
+#pragma mark Control callbacks
+//////////////////////////////////////////////////////////////
+
+- (void)editingSwitchChanged:(UISwitch *)control
+{
+ self.gridView.editing = control.on;
+ control.on = self.gridView.isEditing;
+}
+
+- (void)sortStyleSegmentedControlChanged:(UISegmentedControl *)control
{
switch (control.selectedSegmentIndex)
{
case 1:
- self.gridView.layoutStrategy = [GMGridViewLayoutStrategyFactory strategyFromType:GMGridViewLayoutHorizontal];
+ self.gridView.style = GMGridViewStylePush;
break;
case 0:
default:
- self.gridView.layoutStrategy = [GMGridViewLayoutStrategyFactory strategyFromType:GMGridViewLayoutVertical];
+ self.gridView.style = GMGridViewStyleSwap;
break;
}
}

0 comments on commit 9ee51dd

Please sign in to comment.