I have a custom UIPickerView
with three components that are dependent on one another (i.e., the second one shows values depending on what has been selected in the first one and the third is dependent on the first and second). I am getting the values that are shown in the UIPickerView
out of an NSDictionary
.
Everything works nicely, except, when I spin two of the components at the same time, the app sometimes crashes (no time to reload data). This is what my pickerView:didSelectRow:inComponent
looks like:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSLog(@"Selecting row %d component %d", row, component);
// When A is changed, we need to reload B and C
if (component == 0) {
[pickerView reloadComponent:1];
[pickerView selectRow:0 inComponent:1 animated:YES];
// need to reload the C after reloading B
[self pickerView:pickerView didSelectRow:0 inComponent:1];
}
else if (component == 1) {
[pickerView reloadComponent:2];
[pickerView selectRow:0 inComponent:2 animated:YES];
}
[self updateSelection];
}
Is there a way to prevent the user from spinning more than one component of the picker at a time to prevent a crash?
Thanks!
Good holy Jesus you call
didSelectRow:component:
recursively!I know that the way you handle parameters prevents it from going more than two levels deep, but still, don't do that. Ever.
There's no way to prevent the user from spinning more than one component at a time. It is the joy of multitouch and iOS to set everything in motion and watch it all settle down into a consistent state. And because the spinner components take time to settle, the user can set them all atwitter using just one finger. You must learn to write your code accordingly.
In this case, you want to delete all calls to
didSelectRow:component:
from within itself - it is to be called by the system only. You're on the right track with your use ofreloadComponent:
, it's just that you're not reloading enough of them:I don't know what your
updateSelection
does - presumably it sets the data model based on the value ofpickerView
. This is fine - but it needs to happen at the top of your method, so that when whichever of yourtitleForRow
orviewForRow
is called, it has the proper data to choose from. Also, from within any of those three (update
,titleFor
,viewFor
) do not assert values into the picker! Just read from it.I think this is a good starting point for you, but you will have to tweak things a little here and there as you get closer to your goal. I think you will find that the key to Pickers is that they only assert
didSelectRow:component:
when one of the component spinner things settles down on a value, and they're really good about continuing to spin if you reload them. Give this a shot and see if you can make some progress, send word back if you get stuck.Spinning two wheels at a time means you have multi-touch enabled, right? So, a possibility, may be to just disable multi-touch and limit the user to touching and affecting only one wheel in the UIPickerView.
Here's a link that might help.