
I've been working may way through the UIKit components learning how they work and how to use them. Got a bit carried away with the UIPickerView and ended up building an entire app.
There's two tricks I've developed. The first is a simple means of getting the picker dials to wrap (by default the work like a list - once you reace the end you then have to scroll all the way back up to the beginning again). A number of the native apps, such as the alarm (but interestingly, not the timer) feature wrapping dials, and I thought it seemed like a good idea. The second trick was highlighting the selected row of each dial to make it more prominent and the overall look less like a kids toy.
Second one first. I have a class ResistorBand which all four band types inherit. The band objects are called upon by the controller (which acts as a delegate for the UIPickerView) to supply the views for their corresponding dial. I've subclassed UIView and overwritten - (void)drawRect:(CGRect)rect which actually paints each square. If the view is not the selected view for that row, it has a white rectangle with a 0.6 alpha drawn over it. This has the effect of slightly muting the non-selected colours.
Getting the dials to wrap up being simple in the end, thanks to a suggestion from a friend of mine [thanks Matt].
To start, we need to tell the picker there's three times the number of rows than there really are:
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component {
return [[self bandForIndex:component] viewCount]* 3;
}
Next, when the picker asks for a particular view, we need to map the row it's asking for, with the actual views, ie just row mod viewCount:
return [views objectAtIndex:position % [self viewCount]];
Finally, when the user selects a row, as well as our normal logic, we need to make sure the picker row selected is always in the range [band viewCount] and [band viewCount]*2-1 (inclusive). This means that whenever the picker draws the selectd view, there will always be others above and below it (we'll never reach the end of the list):
- (void)pickerView:(UIPickerView *)pickerView
didSelectRow:(NSInteger)row
inComponent:(NSInteger)component {
ResistorBand *band= [self bandForIndex:component];
[band selectPosition:row % [band viewCount]];
NSInteger updatedRow= row;
if (row< [band viewCount]) {
updatedRow= row+ [band viewCount];
} else if (row>= 2* [band viewCount]) {
updatedRow= row- [band viewCount];
}
[pickerView selectRow:updatedRow
inComponnt:component
animated:NO];
[self updateDisplay];
}
Hope this makes sense.