0

I got an assertion failure when I try to delete a row from a UITableView using UITableViewCellEditingStyleDelete. Hope someone could tell me what is going wrong in my code.

*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:1070
2013-01-30 14:19:21.450 MyApp[48313:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.  The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
*** First throw call stack:
(0x28ee012 0x1d3fe7e 0x28ede78 0x19fef35 0xf51b8d 0xf5df95 0xf5dfc3 0xcfe9 0xf6c384 0x10a9b1b 0x1d53705 0xeb3920 0xeb38b8 0xf74671 0xf74bcf 0xf746a6 0x1144f95 0x1d53705 0xeb3920 0xeb38b8 0xf74671 0xf74bcf 0xf73d38 0xee333f 0xee3552 0xec13aa 0xeb2cf8 0x2d1adf9 0x2d1aad0 0x2863bf5 0x2863962 0x2894bb6 0x2893f44 0x2893e1b 0x2d197e3 0x2d19668 0xeb065c 0x24fd 0x2435)
libc++abi.dylib: terminate called throwing an exception
(lldb) 

Here is my code:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [records count];
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete)
    {

        // Delete record from the sqlite database
        NSNumber *recordDBID = [records objectAtIndex:indexPath.row];
        Record *myRecord = [[Record alloc] initWithDBID:recordDBID database:UIAppDelegate.formManager.connection];
        BOOL valueX = [myRecord deleteRecordInDb:recordDBID];
        [myRecord release];

        // Delete record from array
        NSMutableArray *aNewArray = [[NSMutableArray alloc] initWithArray:records];
        [aNewArray removeObjectAtIndex:indexPath.row];
        records = aNewArray;
        [aNewArray release];

        // Delete the record from the table
        [tableView beginUpdates];
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        [tableView endUpdates];
        [tableView reloadData];   
}
V-Xtreme
  • 7,230
  • 9
  • 39
  • 79
K Hsueh
  • 610
  • 1
  • 10
  • 19
  • The error indicates that you're deleting two objects from `records`. Check that your object doesn't get removed from the array by logging `records` right before you remove the object from it. – Scott Berrevoets Jan 30 '13 at 06:22

2 Answers2

0

I think there's a chance that you may be overcomplicating the removal of the table view row.

You do not need to call [tableView beginUpdates] and [tableView endUpdates] if you are only going to be removing a single row between those two calls, so you can lose them. Begin and end updates are only required if you are going to be carrying out multiple insert or delete actions at the same time.

Secondly, calling [tableView reloadData] at this point is overkill as the table view will automatically request the information it needs as part of the delete action using the assigned delegate/datasource. You can reduce the last part of the code to this:

// Delete the record from the table

[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

It may be that the current row removal code (in combination with the wider code base) is confusing the system.

If the above does not help we'll need to see all code where the records array is being modified. In this event I'll update my answer.

Community
  • 1
  • 1
Trebor
  • 308
  • 3
  • 10
  • Thanks Trebor. Now I know what the beginUpdates and endUpdates do. At the end, I have to give up using deleteRowsAtIndexPaths. I delete the record from the database, update the array by querying the database again (rather than using removeObjectAtIndex to remove the record from it), and use [self.tableView reloadData] to update the UITableView. – K Hsueh Jan 30 '13 at 23:06
  • In order to simplify the situation further you could make 'records' an NSMutableArray to start with and remove the object from the array before calling 'deleteRowsAtIndexPaths'. – Trebor Jan 31 '13 at 00:23
  • Also, think you've got a memory management issue when you attempt to remove the record from the array by creating an intermediary mutable array and removing the object. You then release this array so according to reference counting rules the new array you have created will now be released. This may have something to do with the incorrect row count being returned later to 'UITableView'. – Trebor Jan 31 '13 at 00:27
0

In my case I have typos: it was indexPath instead newIndexPath in insertRowsAtIndexPaths

 func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)
        {
            switch type
            {
            case .Insert:
                self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Bottom)

            case .Delete:
                self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .None)

            case .Update:
                self.tableView.reloadRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)

            case .Move:
                    self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .None)
                    self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Bottom)
                }
        }
ChikabuZ
  • 10,031
  • 5
  • 63
  • 86