Search code examples
iphoneiosobjective-cnsarrayuipickerview

UIPickerView crashes app when user is spinning components at the same time


I've got really unbearable issue with UIPickerView. There're 2 components: first one with food categories, and second with foods inside each category. I've got proper arrays with foods, which looks like:

ViewController.h

@property (strong, nonatomic) NSArray *leftPickerDataSource;
@property (strong, nonatomic) NSArray *vegetablesDataSource;
@property (strong, nonatomic) NSArray *eggsDataSource;
@property (strong, nonatomic) NSArray *pastaDataSource;
@property (strong, nonatomic) NSArray *riceDataSource;
@property (strong, nonatomic) NSArray *meatDataSource;

ViewController.m

...
@implementation ViewController

@synthesize foodPicker;

@synthesize leftPickerDataSource;
@synthesize vegetablesDataSource;
@synthesize eggsDataSource;
@synthesize pastaDataSource;
@synthesize riceDataSource;
@synthesize meatDataSource;

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.leftPickerDataSource = [NSArray arrayWithObjects: @"Vegetables", @"Eggs", @"Pasta", @"Rice", @"Meat", nil];
    self.vegetablesDataSource = [NSArray arrayWithObjects:@"Potatoes", @"Broad bean", @"Beans", @"Broccoli", @"Cabbage", @"Cauliflower", @"Corn", nil];
    self.eggsDataSource = [NSArray arrayWithObjects:@"Soft-boiled", @"Hard-boiled", nil];
    self.pastaDataSource = [NSArray arrayWithObjects:@"Pasta", @"Spaghetti", nil];
    self.riceDataSource = [NSArray arrayWithObjects:@"White", @"Brown", @"Black", @"Red", nil];
    self.meatDataSource = [NSArray arrayWithObjects:@"Sausages", @"Crabs", @"Lobsters", @"Shrimps", nil];
}

I believe arrays ain't the problem, but when I spin two components of picker at the same time, the app usually crashes with EXC_BAD_ACCESS (Code=1...).

Here goes my UIPickerView:

ViewController.m

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

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    if (component == 0) {
        // Left picker
        return [leftPickerDataSource count];
        //[foodPicker selectRow:0 inComponent:1 animated:YES];
    }
    else {
        // Right picker
        NSInteger sRow = [foodPicker selectedRowInComponent:0];
        if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Vegetables"])
            return [vegetablesDataSource count];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Eggs"])
            return [eggsDataSource count];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Pasta"])
            return [pastaDataSource count];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Rice"])
            return [riceDataSource count];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Meat"])
            return [meatDataSource count];
    }
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    if (component == 0) {
        // Left picker
        return [leftPickerDataSource objectAtIndex:row];
    }
    else {
        // Right picker
        NSInteger sRow = [foodPicker selectedRowInComponent:0];
        if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Vegetables"])
            return [vegetablesDataSource objectAtIndex:row];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Eggs"])
            return [eggsDataSource objectAtIndex:row];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Pasta"])
            return [pastaDataSource objectAtIndex:row];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Rice"])
            return [riceDataSource objectAtIndex:row];
        else if ([[leftPickerDataSource objectAtIndex:sRow] isEqual:@"Meat"])
            return [meatDataSource objectAtIndex:row];
    }
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    if (component == 0)
        [foodPicker reloadComponent:1];
}

Can't find anything suspicious here too. Have no idea why, but spinning the wheels crashes the app. I looked for answers at SO, but nothing helps :/

Have you any idea what's the matter?


Solution

  • Actually I was wrong in my supposition -- the selected row is not undefined or nil, but it's changing rapidly while you spin. The problem is that the number you return in numberOfRowsInComponent depends on where the spinning dial is when that method runs, but by the time titleForRow runs, the first component is on a different row, and you're trying to access rows that don't exist. To fix it, you should define a property sRow, assign it a value in numberOfRowsInComponent, and then use that same value (don't re-assign like you're doing now) in titleForRow, rather than getting a new value.

    @property (nonatomic) NSInteger sRow;
    
    - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
    {
        if (component == 0) {
            // Left picker
            return [leftPickerDataSource count];
            //[foodPicker selectRow:0 inComponent:1 animated:YES];
        }
        else {
            // Right picker
            self.sRow = [foodPicker selectedRowInComponent:0];
            if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Vegetables"])
                return [vegetablesDataSource count];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Eggs"])
                return [eggsDataSource count];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Pasta"])
                return [pastaDataSource count];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Rice"])
                return [riceDataSource count];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Meat"])
                return [meatDataSource count];
        }
    }
    
    - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
    {
        if (component == 0) {
            // Left picker
            return [leftPickerDataSource objectAtIndex:row];
        }
        else {
            // Right picker
            if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Vegetables"])
                return [vegetablesDataSource objectAtIndex:row];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Eggs"])
                return [eggsDataSource objectAtIndex:row];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Pasta"])
                return [pastaDataSource objectAtIndex:row];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Rice"])
                return [riceDataSource objectAtIndex:row];
            else if ([[leftPickerDataSource objectAtIndex:self.sRow] isEqual:@"Meat"])
                return [meatDataSource objectAtIndex:row];
        }
    }