July 11, 2008
iPhone SDK 2.0 is a welcome thing but some of the controls make you wonder how they are supposed to be easy to work with. One thing iPhone devs will need that has driven me nuts because so few implement it right yet in any of the apps I’ve seen it in. The odometer style pickerview, no one seems to understand so I’m going to break down the UIPickerView into its most basic components hopefully this helps someone with the spare time to develop for the iphone to create some kewl apps that use it to make the ui experience better.
First here is some sample code that should help you better understand how to virtually wrap the pickerview. What we have in the titleForRow method is an array the returns the value of the selected row modulus 10. This ensures we stay in our array and return the appropriate string label for the row we want. This means no matter what “row” the ui thinks we are in we will be returning what we want them to see for that row giving us the virtual wrap we want. The return could just as easily be simply the row%10 as an NSString without the array for a basic odometer style picker.
Some other code that helps us with the illusion is the numberOfRowsInComponent method. In here we return the number of possible rows to select. I started off using NSMaxIneger and quickly found this breaks everything so I bumped the number down until I got around 16384. This allows you to wrap around and around and force the user into having to be pretty dedicated to reach the end of the list breaking the illusion. Change the number down to 32 to see the end of the list and get a better feel for what is going on in the background as the delegate methods aid in rendering the UI.
Now what you really want to happen is if they hit the end of the list repopulate it and the easiest way to do this is redirect the list to its own middle. But if every time you did this you took them to the middle it would mess up their current selection so the algorithm has to be a little smarter and offset them to their current selection. There are three pieces to this. The first is the didSelectRow this delegate is called when the user stops moving the control. From here we want to call a delegate that is called when the control loads as well pickerViewLoaded. When the pickerViewLoaded delegate is called we setup the same max as the max returned in numberOfRowsInComponent because we want to go to the middle of the possible selections. We then take the max divided by two for the middle then use that value mod 10 to get the offset to take us to the nearest row divisible by ten. From here we can take the current selected row modulus ten and use that as the offset tot he current row we want them to be on. When we select this row without animation there should be no apparent movement to the end user and they should now be back towards the middle of the list.
The final piece of code here should enable us to return the number of horizontal components in the pickerview. As you can see the other methods have a component method that returns which column of data you are looking at. There is additional programming required for dealing with this but it shouldn’t be to difficult to figure out once you understand what you’re dealing with in the basics of the pickerview.
If your anything like me while the interface is unique but the loading mechanism I think could be significantly optimized but I think the user experience is one that as a developer I can live with because even with the overhead on the device it seems to work well enough. I would also like to disclaim this isn’t meant to be a definitive on the subject of the PickerView only a guide so application developers wanting to work with this control can do so without going bald because of the complexities of the control.
I attached the code as a project so building it in xcode and messing around with the parameters shouldn’t be to difficult.