UIPickerView selectRow not working as expected

2019-07-03 23:49发布

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;
}

3条回答
Viruses.
2楼-- · 2019-07-03 23:59

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

查看更多
做个烂人
3楼-- · 2019-07-04 00:20

The pickerView doesn't seem to get time to breathe properly so i'll suggest you let viewDidLoad load the pickerView normally and then do your stuff in -viewDidAppear:.

Well... you want animated:YES anyways so, once again, no point doing this in -viewDidLoad or even -viewWillAppear

Try this:

-(void)viewDidAppear:(BOOL)animated
{
    [picker selectRow:rowA inComponent:COMPONENT_A animated:YES];

    //no point reloading all components, just B
    [picker reloadComponent:COMPONENT_B];

    [picker selectRow:rowB inComponent:COMPONENT_B animated:YES];
}

Also, the place where you do:

NSInteger selectedRowIdx = [self selectedRowInComponent:COMPONENT_A];

shouldn't that be

NSInteger selectedRowIdx = [pickerView selectedRowInComponent:COMPONENT_A];
查看更多
虎瘦雄心在
4楼-- · 2019-07-04 00:21

It might be a late answer but I just faced the issue and solved it in the following way

  1. Implement func pickerView(_:didSelectRow:inComponent) in your view controller.

  2. Call the following two functions consecutively.

    2.1 pickerView.selectRow(selectedIndex, inComponent: 0, animated: true) and

    2.2. yourViewController.pickerView(pickerView, didSelectRow: selectedIndex, inComponent: 0)

查看更多
登录 后发表回答