iOS - Objective-C - Custom Paging with UIScrollView using the IGGridView

Stephen Zaharuk / Tuesday, May 13, 2014

I've said this before, and i'll probably keep saying it, the UIScrollView is very powerful and awesome. Its so powerful that we build our IGGridView control by deriving from it. Even Apple wrote their UITableView the same way.

So, today I'm going to show you how you modify the scrolling of the UIScrollView, and thus the IGGridView, so that you have one column always centered in the viewport. Thats including the first and last item.


You can download my sample project here

In order to build the project, make sure you have NucliOS installed. If you don't currently have a license, don't worry, you can just download it here. It's a quick install, i Promise! :)

Download iOS Controls Now

Ok, lets get started!

1. Make sure you have a reference to the IG.framework and the QuartzCore.framework.

2. In this case, we're going to add our IGGridView and make it take the full frame of the ViewController. That's not necessary, however it does make for a better sample

3. The key to this sample is first to create an inset and set a proper columnWidth for our columns. We do that with the following code:

CGFloat columnWidth = (int)( gridHeight * .75);
CGFloat inset = ( gridWidth/2 - columnWidth/2);

What we're doing here is pretty simple. I'm saying that i want the column width to be 75% of the grid's height, with the assumption that my grid's width is larger than my height. That percentage is arbitrary and can be whatever you'd like.

Then we need to figure out what our inset it will be. This is basically how we're going to make sure that both the first and last columns stop scrolling with those respective items being centered.

Then we need to set those values on the gridView:

_gridView.contentInset = UIEdgeInsetsMake(0, inset, 0, inset);
_gridView.columnWidth = [IGColumnWidth CreateWithFixedWidth:columnWidth];
_gridView.rowHeight = gridHeight;

We are also setting the rowHeight b/c in this sample we're going to use an IGGridViewSingleRowSingleFieldDataSourceHelper. We're going to use an ImageColumn so that our Image data is displayed, however you could do this with any type of cell, including any custom column or cells you create with the GridView.

The next step is we're going to implement the IGGridViewDelegate protocol and set that to the IGGridView. This protocol actually derives from the UIScrollViewDelegate which allows it to act as a doubly useful, as for this sample we're going to implement a scrollView delegate member and a gridView delegate member. 

First, we're going to override the scrollView's -(void)scrollViewWillEndDragging:withVelocity:targetContentOffset: selector. This will allow us to create a custom paging experience: 

-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
   CGFloat pageWidth = _gridView.columnWidth.value;

   CGFloat val = (scrollView.contentOffset.x + scrollView.contentInset.left) / pageWidth;
   NSInteger newPage = val;

   if (velocity.x == 0)
      newPage = floor((targetContentOffset->x - pageWidth / 2) / pageWidth) + 1;
      if(velocity.x < 0)
         CGFloat diff = val - newPage;
         if(diff > .5)

      newPage = velocity.x > 0 ? newPage + 1 : newPage - 1;

      if (newPage < 0)
         newPage = 0;
      if (newPage > scrollView.contentSize.width / pageWidth)
         newPage = ceil(scrollView.contentSize.width / pageWidth) - 1.0;

   _currentItemIndex = newPage;

   *targetContentOffset = CGPointMake([self resolveContentOffsetForPage:_currentItemIndex], targetContentOffset->y);

   return (int)(page * _gridView.columnWidth.value) - _gridView.contentInset.left;

Basically, what we're doing here is adjusting where the scrollView will stop scrolling. And we do this based off the contentSize of the scrollView/gridView, the width of our column and the velocity.

There is one other thing you may want to set as well, to really simulate normal UIScrollView paging, and thats to set the decelerationRate:

_gridView.decelerationRate = UIScrollViewDecelerationRateFast;

You would set this property when you setup the rest of the IGGridView. 

As a last step, and to just add a nicer feeling, when a user selects a cell, we'll scroll that cell to the center. 

To do that its pretty simple:

-(IGCellPath *)gridView:(IGGridView *)gridView willSelectCellAtPath:(IGCellPath *)path
   [_gridView setContentOffset:CGPointMake([self resolveContentOffsetForPage:path.columnIndex], 0) animated:YES];

  _currentItemIndex = path.columnIndex;

return nil;

All we have to do is resolve what the offset would be for that particular column and have it set the contentOffset of the scrollView/gridView to that new value. 

And thats it. 

I hope you found this useful!

By Stephen Zaharuk (SteveZ)