0

Basically, I am trying to write an application that has a tableview with two sections, one with uncompleted tasks (cells) and the other with completed ones. Each cell has a checkbox UIButton subclass. The problem is that it crashes with a NSRangeException when I check off more than half the items. I would really appreciate any advice on this issue.

Error: * Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 9 beyond bounds [0 .. 8]'

CheckMarkTapped Method (called to switch an item):

-(void)checkMarkTapped:(UIButton *)sender {

    UITableViewCell *cell = ((UITableViewCell *)[sender superview]);
    NSIndexPath *originIndexPath = [self.tableView indexPathForCell:cell];
    NSIndexPath *destinationIndexPath;

    NSLog(@"originIndexPath row: %i, section: %i", originIndexPath.row, originIndexPath.section);

    [self fixAllCheckboxes];

    if (originIndexPath.section == 0) {

        destinationIndexPath = [NSIndexPath indexPathForRow:[completedItems count] inSection:1];
        [sender setSelected:NO];

        NSMutableDictionary *tempDict = [[NSDictionary dictionaryWithDictionary:[uncompletedItems objectAtIndex:originIndexPath.row]]mutableCopy];
        [completedItems addObject:tempDict];
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObjects:destinationIndexPath, nil] withRowAnimation:UITableViewRowAnimationLeft];
        [uncompletedItems removeObjectAtIndex:originIndexPath.row];


    }

    if (originIndexPath.section == 1) { //if the checkbox is in section 1
        [sender setSelected:YES];

        NSMutableDictionary *tempDict = [NSDictionary dictionaryWithDictionary:[[completedItems objectAtIndex:originIndexPath.row]mutableCopy]];

        destinationIndexPath = [NSIndexPath indexPathForRow:[[tempDict objectForKey:@"index"]intValue] inSection:0];
        [uncompletedItems insertObject:tempDict atIndex:[[tempDict objectForKey:@"index"]intValue]];
        [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObjects:destinationIndexPath, nil] withRowAnimation:UITableViewRowAnimationLeft];
        [completedItems removeObjectAtIndex:[self.tableView indexPathForCell:cell].row];

    }

    [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:originIndexPath, nil] withRowAnimation:UITableViewRowAnimationLeft];
}

CELLFORROWATINDEXPATH Method:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    static NSString *CellIdentifier = @"Cell";

    if (indexPath.section == 1) {
        CellIdentifier = @"Cell1";
    }

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
        cell.showsReorderControl = YES;
    }

    for (UIControl *checkbox in [cell subviews]) {
        if (checkbox.tag == 9001) {
            [checkbox removeFromSuperview];
        }
    }

    if (indexPath.section == 0) {
        UIButton *checkbox = [[UIButton alloc]initWithFrame:CGRectMake(5, 5, 40, 40)];
        [checkbox addTarget:self action:@selector(checkMarkTapped:) forControlEvents:UIControlEventTouchUpInside];
        [cell setIndentationWidth:45];
        [cell setIndentationLevel:1];

        [checkbox setTag:9001];
        [checkbox setImage:[UIImage imageNamed:@"unchecked"] forState:UIControlStateNormal];
        [checkbox setImage:[UIImage imageNamed:@"checked"] forState:UIControlStateSelected];

        [cell addSubview:checkbox];

        cell.textLabel.backgroundColor = [UIColor whiteColor];
        cell.textLabel.backgroundColor = [UIColor whiteColor];
        cell.contentView.backgroundColor = [UIColor whiteColor];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:20];

        switch (indexPath.section) {
            case 0:
                cell.textLabel.text = [[uncompletedItems objectAtIndex:indexPath.row]objectForKey:@"Name"];
                break;
            case 1:
                cell.textLabel.text = [[completedItems objectAtIndex:indexPath.row]objectForKey:@"Name"];
                [checkbox setSelected:YES];
                break;
            default:
                break;
        }

        [cell addSubview:checkbox];

    } else if (indexPath.section == 1) {

        UIButton *checkbox = [[UIButton alloc]initWithFrame:CGRectMake(5, 5, 40, 40)];
        [checkbox addTarget:self action:@selector(checkMarkTapped:) forControlEvents:UIControlEventTouchUpInside];
        [cell setIndentationWidth:45];
        [cell setIndentationLevel:1];
        [checkbox setTag:9001];

        cell.textLabel.backgroundColor = [UIColor whiteColor];
        cell.textLabel.backgroundColor = [UIColor whiteColor];
        cell.contentView.backgroundColor = [UIColor whiteColor];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:20];
        cell.textLabel.text = [[uncompletedItems objectAtIndex:indexPath.row]objectForKey:@"Name"];

        [checkbox setImage:[UIImage imageNamed:@"unchecked"] forState:UIControlStateNormal];
        [checkbox setImage:[UIImage imageNamed:@"checked"] forState:UIControlStateSelected];

        [cell addSubview:checkbox];

        switch (indexPath.section) {
            case 0:
                cell.textLabel.text = [[uncompletedItems objectAtIndex:indexPath.row]objectForKey:@"Name"];
                break;
            case 1:
                cell.textLabel.text = [[completedItems objectAtIndex:indexPath.row]objectForKey:@"Name"];
                [checkbox setSelected:YES];
                break;
            default:
                break;
        }

        [cell addSubview:checkbox];
    }
    return cell;
}

Here is the NSLog for the Crash. As you can see, it happens when the arrays have equal amounts of items:

2012-12-26 12:54:44.583 Checklist R4[1128:c07] Destination Row: 0, UnCompleted Count: 19, Completed Count: 1
2012-12-26 12:54:45.034 Checklist R4[1128:c07] Destination Row: 1, UnCompleted Count: 18, Completed Count: 2
2012-12-26 12:54:45.454 Checklist R4[1128:c07] Destination Row: 2, UnCompleted Count: 17, Completed Count: 3
2012-12-26 12:54:45.873 Checklist R4[1128:c07] Destination Row: 3, UnCompleted Count: 16, Completed Count: 4
2012-12-26 12:54:46.294 Checklist R4[1128:c07] Destination Row: 4, UnCompleted Count: 15, Completed Count: 5
2012-12-26 12:54:47.072 Checklist R4[1128:c07] Destination Row: 5, UnCompleted Count: 14, Completed Count: 6
2012-12-26 12:54:47.399 Checklist R4[1128:c07] Destination Row: 6, UnCompleted Count: 13, Completed Count: 7
2012-12-26 12:54:48.085 Checklist R4[1128:c07] Destination Row: 7, UnCompleted Count: 12, Completed Count: 8
2012-12-26 12:54:48.621 Checklist R4[1128:c07] Destination Row: 8, UnCompleted Count: 11, Completed Count: 9
2012-12-26 12:54:49.090 Checklist R4[1128:c07] Destination Row: 9, UnCompleted Count: 10, Completed Count: 10
2012-12-26 12:54:50.234 Checklist R4[1128:c07] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 9 beyond bounds [0 .. 8]'
Hersh Bhargava
  • 615
  • 4
  • 19
  • Two things: first of all show or say how many rows/section you return, second say in what line of code you get the exception (if you don't know it add an exception breakpoint). – Ramy Al Zuhouri Dec 26 '12 at 03:30

2 Answers2

2

your completedItems or uncompletedItems array, either one having only 8 items, but you are trying to access the item more than that. Check your numberOfRowsInSection method, give their dynamic value, like

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    int rowCount = 0;
    if (section == 0) {
        rowCount = [uncompletedItems count];
    } else {
        rowCount = [completedItems count];
    }
    return rowCount;
}

and moreover its not needed both switch and if .Use anyone to check your section is 0 or 1.

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

if (indexPath.section == 1) {
    CellIdentifier = @"Cell1";
}

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    cell.showsReorderControl = YES;
}

for (UIControl *checkbox in [cell subviews]) {
    if (checkbox.tag == 9001) {
        [checkbox removeFromSuperview];
    }
}

    UIButton *checkbox = [[UIButton alloc]initWithFrame:CGRectMake(5, 5, 40, 40)];
    [checkbox addTarget:self action:@selector(checkMarkTapped:) forControlEvents:UIControlEventTouchUpInside];
    [cell setIndentationWidth:45];
    [cell setIndentationLevel:1];

    [checkbox setTag:9001];
    [checkbox setImage:[UIImage imageNamed:@"unchecked"] forState:UIControlStateNormal];
    [checkbox setImage:[UIImage imageNamed:@"checked"] forState:UIControlStateSelected];

    [cell addSubview:checkbox];

    cell.textLabel.backgroundColor = [UIColor whiteColor];
    cell.textLabel.backgroundColor = [UIColor whiteColor];
    cell.contentView.backgroundColor = [UIColor whiteColor];
    cell.textLabel.font = [UIFont boldSystemFontOfSize:20];

    switch (indexPath.section) {
        case 0:
            cell.textLabel.text = [[uncompletedItems objectAtIndex:indexPath.row]objectForKey:@"Name"];
            break;
        case 1:
            cell.textLabel.text = [[completedItems objectAtIndex:indexPath.row]objectForKey:@"Name"];
            [checkbox setSelected:YES];
            break;
        default:
            break;
    }

    [cell addSubview:checkbox];

return cell;
}
arthankamal
  • 6,341
  • 4
  • 36
  • 51
  • I checked the cellForRowAtIndexPath method, and found it to be fine. Any other ideas? – Hersh Bhargava Dec 26 '12 at 07:10
  • Try by setting [Symbolic Breakpoints](http://stackoverflow.com/questions/13929518/nsarraym-insertobjectatindex-object-cannot-be-nil-how-determine-where-i/13930255#13930255). I couldn't find what's problem in your code. Because in your button action, you are adding sometimes and you are inserting sometimes. `Symbolic Breakpoints` will get you where the code is crashing. – arthankamal Dec 27 '12 at 03:10
0

There might be two problems in your code

  1. You are returning numbers of rows to Table View less then your array count.
  2. Your array is exceeding from the numbers of rows in Table View.

Your described error clearly shows that your array have 8indexes but you are accessing the 9th one which causes the error.

iOmi
  • 625
  • 10
  • 24
  • I get what causes the error, but I cannot figure out why it is accessing a ninth index that doesn't exist. Also, it is strange that this only happens when there are equal items in both sections. – Hersh Bhargava Dec 26 '12 at 07:09
  • ok fine, one quick solution is that we can put a check , that insert data in tableView only if the `index` is less then the `Array count`. – iOmi Dec 26 '12 at 07:19
  • It didn't work. It crashes before. I edited the post to include the point where it crashes. – Hersh Bhargava Dec 26 '12 at 20:54