NUCLiOS IGGridView: Using the Grid as a UIPickerView

Stephen Zaharuk / Thursday, October 16, 2014

iOS has control called the UIPickerView. It used to be pretty ugly, but in iOS 7 they made it look a lot nicer. However, it still has it's limitations. You can't set a height on it, and its still kind of cumbersome to setup and style. 

Well, in 14.2 we introduced a new feature that lets you control how Cell's look based on their location within the viewport of the IGGridView. Which actually allows you to pretty easily create your own UIPicker, without any limitations. 

In fact, you'll have the full power of the IGGridView and the full styling capabilities. It's a win/win situation. 

So how do we do this? Turns out its really easy. You just create a custom cell, and override a single method. 

So lets get started!

Our cell will just derive from IGGridViewCell. Since we're just going to display text, we only need to derive from the single method:

@interface IGPickerCell : IGGridViewCell

@end

-(CGFloat)centerLocation:(CGFloat)val

{

    if(val >= .5)

    {

        val = 1 - val;

    }

    

    if(val .5)

    {

        val = val/.5;

    }

    

    return val;

}

- (void)cellLocationInViewportChanged:(CGPoint)location

{

    CGFloat fontSizePercent = [self centerLocation:location.y];

    CGFloat maxFontSize = 25;

    CGFloat actualFontSize = maxFontSize * fontSizePercent;

    self.textLabel.font = [UIFont fontWithName:@"Avenir-Book" size:actualFontSize];

}

The centerLocation method is just a helper method to help me coerce the location i was give. 

The real logic is in the cellLocationViewportChanged method. As you can see, its only 4 lines of code. 

Isn't that awesome?

So yea, if you run it now, you'll see that neat scrolling trick where the cell's font will get larger as it gets towards the center of the grid's viewport.  However, we can do a bit more to make it really act like a UIPickerView. 

Because the IGGridView is in fact a UIScrollView, we have some extra power we can wield. For one, we can set a ContentInset, so the IGGridView will let cells that are at the top and the bottom, to stop in the center of the viewport. 

Turns out that's really easy too!

CGFloat inset = (_gridView.bounds.size.height - _gridView.rowHeight) / 2;

_gridView.contentInset = UIEdgeInsetsMake(inset, 0, inset, 0);

Also, we don't want cells to stop scrolling halfway through the center. They should always snap to the center of the viewport. Well, it turns out the UIScrollView has a really good method for doing that. Basically you can detect where the UIScrollView will stop scrolling and adjust it to a different position.  To get at this method, you just need to implement the IGGridViewDelegate.

Here is the code for that:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset

{

    CGFloat pageSize = _gridView.rowHeight;

    CGFloat val = (scrollView.contentOffset.y + scrollView.contentInset.top) / pageSize;

    NSInteger newPage = val;

    if (velocity.y == 0)

    {

        newPage = ceil((targetContentOffset->y - pageSize / 2) / pageSize) + 1;

    }

    else if (velocity.y > 0)

    {

        newPage = ceil((targetContentOffset->y - pageSize / 2) / pageSize) + 1;

    }

    else if (velocity.y 0)

    {

        newPage = floor((targetContentOffset->y - pageSize / 2) / pageSize) - 1;

    }

    if (newPage 0)

        newPage = 0;

    if (newPage > scrollView.contentSize.height / pageSize)

        newPage = ceil(scrollView.contentSize.height / pageSize) - 1.0;

    CGFloat yStop = (newPage * _gridView.rowHeight) - _gridView.contentInset.top

    *targetContentOffset = CGPointMake(targetContentOffset->x, yStop);

}

And finally, if you want a box around the selected item, you can add this piece of code:

UIView *selectionView = [[UIView alloc] init];

selectionView.layer.borderColor = [UIColor colorWithWhite:.5 alpha:1].CGColor;

selectionView.layer.borderWidth = 1;

selectionView.userInteractionEnabled = NO;

selectionView.frame = CGRectMake(0, _gridView.bounds.size.height/ 2 - _gridView.rowHeight / 2, width, _gridView.rowHeight);

selectionView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleTopMargin;

[_gridView addSubview:selectionView];

Enjoy!

-SteveZ