Search code examples
objective-cios5uitableviewnsrangeexception

iOS - NSRangeException only when breakpoints are disabled


Recently started developing apps, so excuse my ignorance. I have a tableView, and when a cell in the table view is clicked, I want to insert a new row directly below it. This currently works in my code. However, I also want the row that has been inserted to be removed once the cell has been clicked again. This is giving me the NSRangeException saying I am out of bounds in my array.

I figured this probably has to do with my tableView delegate/data methods, so I set up break points at each of them. With the break points enabled, the cell is removed perfectly. However, when I disable the breakpoints, and let the application run on its own, it crashes. How could break points possibly be affecting this?

Here is the relevant code:

- (NSInteger) numberOfSectionsInTableView:(UITableView *)songTableView{
    return 1;
}

- (NSInteger)tableView:(UITableView *)songTableView
 numberOfRowsInSection:(NSInteger)section{
    bool debug = false;

    if (debug) NSLog(@"--TableView: rankings");
    if (expandedRow == -1) 
        return [self.songs count];
    else //one row is expanded, so there is +1
        return ([self.songs count]+1);

}

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

        bool debug = false;
        if (debug) NSLog(@"--tableView: tableView");

        NSUInteger row = [indexPath row];
        if (row == expandedRow){ //the expanded row, return the custom cell
            UITableViewCell *temp = [[UITableViewCell alloc] 
                                     initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"test"];
            return temp;
        }
        UITableViewCell *cell = [tableViewCells objectAtIndex:row];
        return cell;
    } 
}

- (NSString *)tableView:(UITableView *)songTableView
titleForHeaderInSection:(NSInteger)section{
        //todo: call refresh title
        return @"The Fresh List";
}

- (CGFloat)tableView:(UITableView *)songTableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath{
        return 44.0; //same as SongCell.xib

}


- (void)tableView: (UITableView *)songTableView 
didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
    bool debug = true;
    //todo: if the user selects expanded cell, doesn't do anything
        SongCell *cell = (SongCell *)[songTableView cellForRowAtIndexPath:indexPath];
        if (cell->expanded == NO){
            //change cell image
            cell.bgImage.image = [UIImage imageNamed:@"tablecellbg_click.png"];
            cell->expanded = YES;

            //add new cell below

            NSInteger atRow = [indexPath row] + 1;
            NSIndexPath *insertAt = [NSIndexPath indexPathForRow:atRow inSection:0];
            NSArray *rowArray = [[NSArray alloc] initWithObjects:insertAt, nil];

            if (debug) NSLog(@"Expanded row: %d", atRow);
            expandedRow = atRow;

            [tableView insertRowsAtIndexPaths:rowArray withRowAnimation:UITableViewRowAnimationTop];

        }else { //cell is already open, so close it
            //change cell image
            cell.bgImage.image = [UIImage imageNamed:@"tablecellbg.png"];
            cell->expanded = NO;

            NSIndexPath *removeAt = [NSIndexPath indexPathForRow:expandedRow inSection:0];
            NSArray *rowArray = [[NSArray alloc] initWithObjects:removeAt, nil];
            if(debug) NSLog(@"--about to delete row: %d", expandedRow);

            expandedRow = -1;
            [tableView deleteRowsAtIndexPaths:rowArray withRowAnimation:UITableViewRowAnimationTop];

            //remove expaned cell below
        }

}

Solution

  • I hate to answer my own questions but I figured it out.

    I was loading up my tableView from an array of objects. When I added the cell, that array still only held 30 objects, whereas my table held 31. I was correctly returning the numberOfRowsInSection method.

    Here is the modification I made. Notice the extra else if:

    NSUInteger row = [indexPath row];
            if (row == expandedRow){ //the expanded row, return the custom cell
                if(debug) NSLog(@"row == expandedRow");
                UITableViewCell *temp = [[UITableViewCell alloc] 
                                         initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"test"];
                return temp;
            }
            else if (expandedRow != -1 && row > expandedRow)
                row--;
    

    My array of objects and the UITableViewCells were suppose to match up 1-1. After the expanded row, indexPath's row because off by 1. Here is my quick fix to this problem, although I'm sure there is a better way to solve this.