Home / Blog / UIPickerView

Urbanspoon style UIPickerView

by Ryan McDonald on 8th May 2009

A requirement for a recent application we worked on was to produce a spinning picker view for randomly selecting items similar to the popular Urbanspoon application.  There were several parts to our solution, making the picker components circular, blurring and animating the spinning, and correctly displaying the final result.  This post will deal with our solution to these items.

The solution to the circular picker came from this post on Jeff LaMarche’s iPhone Development blog.

To achieve the spinning effect we use a series of images animated using UIImageView and placed on top of the UIPickerView.

The most challenging part of the solution was properly displaying the UIPickerView after the animation was complete.  On our first attempt we were seeing random rows of the picker were left blank as in the image below.

missing-rows

The reason for this appears to be due to the fact a view cannot be drawn on the screen twice.  When setting the selected row any rows that were  displayed before the new selection would potentially be drawn blank.  Our solution?  Landing on the final selected row is a two step process.  We know the row that we will land on, and how many rows are displayed at once. The first step is to select a row far enough away from our destination  so that all the rows that will be displayed are not.  In our case that was six.  Then we select the row that we want to end up on.  The code to this would be something like this:

-(void)selectionReadyRow
{
[pickerView selectRow:(selectedRow1 -6) forComponent:0 animated:NO];
[pickerView selectRow:(selectedRow2 -6) forComponent:1 animated:NO];
[self performSelector:@selector(moveIntoPosition) withObject:nil afterDelay:0.5f];
}


-(void)moveIntoPosition
{
[pickerView selectRow:(selectedRow1) forComponent:0 animated:NO];
[pickerView selectRow:(selectedRow2) forComponent:1 animated:NO];
//stop and hide animating image
}

Note the delay of 0.5s using the performSelector:withObject:AfterDelay method.  This seemed to be necessary or else the row selection performed in moveIntoPosition would not always occur.

In writing this post I though another solution to the missing row problem could be to have two copies of every view.  This would eliminate the need of having to position the row twice.  You could choose an alternate copy of a view each time it gets displayed.  This seems like it could be a better solution when you have fewer views to display.

If you use the solution detailed above or the alternate proposed solution we would love to hear how it works out for you in the comments.