iosobjective-cuitableviewuisegmentedcontrolnsrangeexception

NSRangeException in cellForRowAtIndexPath with selectedSegmentIndex


I try to switch cell.textLabel.text (two different arrays) with selectedSegmentIndex. The first one with selectedSegmentIndex = 1 works fine.

but the second makes a crash :

uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010ed70c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x000000010e92abb7 objc_exception_throw + 45
    2   CoreFoundation                      0x000000010ec5a093 -[__NSArrayM objectAtIndex:] + 227
    3   ChillN                              0x000000010be0363b -[EditFriends tableView:cellForRowAtIndexPath:] + 187
    4   UIKit                               0x000000010d2b99e8 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 508
    5   UIKit                               0x000000010d298208 -[UITableView _updateVisibleCellsNow:isRecursive:] + 2853

...

Here is my code : viewDidLoad :

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.currentUser = [PFUser currentUser];

    PFQuery *query = [PFUser query];
    [query orderByAscending:@"name"];
//    [query whereKey:@"objectId" notEqualTo:self.currentUser.objectId];
    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
        if (error) {
            NSLog(@"Error: %@ %@", error, [error userInfo]);
        }
        else {
            self.allUsers = objects;
            [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
        }
    }];

    self.tableData = [[NSMutableArray alloc] init];
    [self getPersonOutOfAddressBook];
}

numberOfRowsInSection :

- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    switch (self.segment.selectedSegmentIndex)
    {
        case 0:
            return [self.allUsers count];
            break;
        case 1:
            return [self.tableData count];
            break;
    }
    return 0;
}

cellforrowatindexpath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

//        UIImageView *accessoryView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
//        [accessoryView setImage:[UIImage imageNamed:@"circle_arrow_right.png"]];
//        [cell setAccessoryView:accessoryView];

    PFUser *selected = [self.allUsers objectAtIndex:indexPath.row];
    if ([self isFriend:selected]) {
        cell.accessoryType = UITableViewCellAccessoryCheckmark;
    } else {
        cell.accessoryType = UITableViewCellAccessoryNone;
    }

    Person *person = [self.tableData objectAtIndex:indexPath.row];
    switch (self.segment.selectedSegmentIndex)
    {

        case 0:
            cell.textLabel.text = [[self.allUsers objectAtIndex:indexPath.row] valueForKey:@"surname"];
            break;

        case 1:
            cell.textLabel.text = person.fullName;
            break;
    }
    return cell;
}

index changed:

-(IBAction) segmentedControlIndexChanged
{
    if(self.segment.selectedSegmentIndex == 0)
    {
        self.tableView.hidden = NO;
    }
    else if (self.segment.selectedSegmentIndex == 1)
    {

        self.tableView.hidden = NO;
    }
    [self.tableView reloadData];
}

How can I fix it ? :)


Solution

  • Your code could crash on these lines

    PFUser *selected = [self.allUsers objectAtIndex:indexPath.row]; or Person *person = [self.tableData objectAtIndex:indexPath.row];

    This because you don't check if selectedSegmentIndex was changed. So if tableview datasource will use self.allUsers then [self.tableData objectAtIndex:indexPath.row]; could crash, if it will use self.tableData then [self.allUsers objectAtIndex:indexPath.row]; could crash

    I've fixed your code:

       - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
        {
            static NSString *CellIdentifier = @"Cell";
            UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
            switch (self.segment.selectedSegmentIndex)
            {
                case 0:
    
            PFUser *selected = [self.allUsers objectAtIndex:indexPath.row];
            if ([self isFriend:selected]) {
                cell.accessoryType = UITableViewCellAccessoryCheckmark;
            } else {
                cell.accessoryType = UITableViewCellAccessoryNone;
            }
            cell.textLabel.text = [[self.allUsers objectAtIndex:indexPath.row] valueForKey:@"surname"];
                    break;
    
                case 1:
                    Person *person = [self.tableData objectAtIndex:indexPath.row];
                    cell.textLabel.text = person.fullName;
                    break;
            }
            return cell;
        }