Friday, March 4, 2011

Inserting and deleting UITableViewCell at the same time not working

Hiya,

I'm having quite a bit of pain inserting and deleting UITableViewCells from the same UITableView!

I don't normally post code, but I thought this was the best way of showing where I'm having the problem:


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 5;
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

    if (iSelectedSection == section) return 5;
    return 1;
}


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

    //NSLog(@"drawing row:%d section:%d", [indexPath row], [indexPath section]);

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }

    if (iSelectedSection == [indexPath section]) {
     cell.textColor = [UIColor redColor];
    } else {
     cell.textColor = [UIColor blackColor];
    } 


    cell.text = [NSString stringWithFormat:@"Section: %d Row: %d", [indexPath section], [indexPath row]];

    // Set up the cell
    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    // Navigation logic -- create and push a new view controller

    if ([indexPath row] == 0) {

     NSMutableArray *rowsToRemove = [NSMutableArray array];
     NSMutableArray *rowsToAdd = [NSMutableArray array];

     for(int i=0; i<5; i++) {

      //NSLog(@"Adding row:%d section:%d ", i, [indexPath section]);
      //NSLog(@"Removing row:%d section:%d ", i, iSelectedSection);

      [rowsToAdd addObject:[NSIndexPath indexPathForRow:i inSection:[indexPath section]]];
      [rowsToRemove addObject:[NSIndexPath indexPathForRow:i inSection:iSelectedSection]];

     }

     iSelectedSection = [indexPath section];

     [tableView beginUpdates];
     [tableView deleteRowsAtIndexPaths:rowsToRemove withRowAnimation:YES];
     [tableView insertRowsAtIndexPaths:rowsToAdd withRowAnimation:YES];
     [tableView endUpdates];

    }
}


This code creates 5 sections, the 1st (indexed from 0) with 5 rows. When you select a section - it removes the rows from the section you had previously selected and adds rows to the section you just selected.

Pictorally, when I load up the app, I have something like this:

http://www.freeimagehosting.net/uploads/1b9f2d57e7.png

Image here: http://www.freeimagehosting.net/uploads/1b9f2d57e7.png

After selecting a table row 0 of section 2, I then delete the rows of section 1 (which is selected by default) and add the rows of section 2. But I get this:

http://www.freeimagehosting.net/uploads/6d5d904e84.png

Image here: http://www.freeimagehosting.net/uploads/6d5d904e84.png

...which isn't what I expect to happen! It seems like the first row of section 2 somehow remains - even though it definitly gets deleted.

If I just do a [tableView reloadData], everything appears as normal... but I obviously forefit the nice animations.

I'd really appreciate it if someone could shine some light here! It's driving me a little crazy!

Thanks again, Nick.

From stackoverflow
  • In the code you posted, your loop index runs from 0 to 4, which suggests that it would delete all of the rows in section 1, and then add five new rows to section 2. Since each section already has a row 0, this would add a second instance of section 2, row 0 to the table.

    I would suggest having your loop run from 1 to 4:

    for (int i=1; i<5; i++)
    {
        // ...
    }
  • Hey eJames, thanks for your comment - a really valid point!!

    I thought for a minute it might solve my problem - but I still have the same trouble. I think there actually might be a iPhone but when you try and remove items from one section and adding them to another section at the same time...

    Chris Hanson : This should probably be a comment on eJames' response, rather than a response in its own right. :)
    e.James : Agreed, but them's the rules here. You can't leave comments with less than 50 rep.
  • I seem to remember that numberOfRowsInSection: will get called when you call deleteRows or insertRow, you need to be really careful that the reality numberOfRowsInSection cliams matches your changes. In this case you may want to try moving the iSelectedSection = [indexPath section]; line to after the endUpdates.

  • I don't remember where I read this but I believe you shouldn't perform table row updates (insertions and deletions) from inside one of the table view delegate functions. I think a better alternative would be to do a performSelectorOnMainThread passing along the necessary information needed to perform the updates as an object. Something like:

    - (void)tableView:(UITableView *)tableView 
      didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
        // ....
        [self performSelectorOnMainThread: @selector(insertRows:)
                               withObject: someObjectOrNil]; // double check args
    }
    
    - (void) insertRows: (NSObject*)someObjectOrNil {
        [tableView beginUpdates];
        // update logic
        [tableView endUpdates];
    
        // don't call reloadData here, but ensure that data returned from the 
        // table view delegate functions are in sync
    }
    
  • FYI: This bug seems to have been fixed completely with the 2.2 iPhone update. Thanks Apple! Nick.

  • Struggled to get this to work. Here's my code to add a row to my tableView (artistsToAdd is the dataSource for the tableView. The number of rows is set with [artistsToAdd count])

    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
    [tableView beginUpdates];
    [artistsToAdd insertObject:[artistField text] atIndex:0];
    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationTop];
    [tableView endUpdates];
    

0 comments:

Post a Comment