Search code examples
objective-cuipickerviewios8uipickerviewdatasource

UIPickerView selectRow not working as expected


I've created a UIPickerView which has two components, the first component (A) has a fixed number of rows set at 13. The other component (B) has a variable number of rows which is dependant on the row selected in (A).

When loading the UIPickerView I am calling the following so that I can default the selection in both components, however the issue Im having is that only component (A) shows the correct values. Component (B) doesn't show the correct set of rows or the correct selection.

[picker selectRow:rowA inComponent:COMPONENT_A animated:YES];
[picker reloadAllComponents];
[picker selectRow:rowB inComponent:COMPONENT_B animated:YES];

I have tried printing out the following after calling this code and it seems that the correct values are printed out, yet when the picker shows only component (A) is showing the correct values and selection.

NSLog(@"(A) - row selected: %i", [picker selectedRowInComponent:COMPONENT_A]);
NSLog(@"(A) - number of rows: %i", [picker numberOfRowsInComponent:COMPONENT_A]);

NSLog(@"(B) - row selected: %i", [picker selectedRowInComponent:COMPONENT_B]);
NSLog(@"(B) - number of rows: %i", [picker numberOfRowsInComponent:COMPONENT_B]);

Does anyone have any ideas on how to debug this or what the issue may be?

Update

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 2;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    switch (component) {
        case COMPONENT_A:
        {
            return 13;
        }
        case COMPONENT_B:
        {
            NSInteger selectedRowIdx = [picker selectedRowInComponent:COMPONENT_A];

            switch (selectedRowIdx) {
                case A:   return 2;
                case B:   return 4;
                case C:   return 6;
                case D:   return 8;
                case E:   return 10;
                case F:   return 12;
                case G:   return 14;
                case H:   return 16;
                case I:   return 18;
                case J:   return 20;
                case K:   return 22;
                case L:   return 24;
                default:    return 1;
            }
        }
    }

    return -1;
}


#pragma mark UIPickerViewDelegate

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    switch (component) {
        case COMPONENT_A:
        {
            switch (row) {
                case A:   return @"A";
                case B:   return @"B";
                case C:   return @"C";
                case D:   return @"D";
                case E:   return @"E";
                case F:   return @"F";
                case G:   return @"G";
                case H:   return @"H";
                case I:   return @"I";
                case J:   return @"J";
                case K:   return @"K";
                case L:   return @"L";
                default:    return @"";
            }
        }
        case COMPONENT_B:
        {
            if (row == 0) {
                return @"";
            } else {
                return [NSString stringWithFormat:@"%i", (int)row];
            }
        }
    }

    return nil;
}

Solution

  • One of the weird eccentricities of UIPickerView is that when you call

    `[.. selectRow: inComponent: animated: ]`
    

    this will trigger a call on the delegate's

    [pickerView: didSelectRow: inComponent: ]
    

    As a result of this it can be easy to create inadvertent race conditions, so check in your delegate's pickerView: didSelectRow: inComponent: method and be aware that this is being called in the middle here as a result of setting the first component. You might be better off storing a variable for what is selected in component0 and switching off that rather than off what is selected in the picker. You might also consider storing a boolean (listenToPicker) so you can switch it off temporally by putting the didSelect stuff inside a if (listenToPicker){ ..... }, then before setting it up with the selectRow: animated: calls you can set listenToPicker to NO, restoring it to YES after all components are set in viewDidAppear . Good luck