0

When animating the deletion of all rows and hence the deletion of all sections that included all rows of a UITableView I am running into this error:

CRASH: attempt to delete row 2 from section 0, but there are only 0 sections before the update

In particular I have a singleton manager class that serves as the table view data source and delegate. I post an NSNotification to tell the table view to delete rows that should be deleted, and that NSNotification triggers the following method:

 dispatch_async(dispatch_get_main_queue(), ^{
            if ([[[Manager sharedManager] justDeletedIndices] count] > 0) {
                [mainTableView beginUpdates];
                NSMutableArray <NSIndexPath *> *pathsToDelete = [[Manager sharedManager] justDeletedIndices];
                [mainTableView deleteRowsAtIndexPaths:pathsToDelete withRowAnimation:UITableViewRowAnimationFade];
                [[Manager sharedManager] setJustDeletedIndices:[NSMutableArray new]];
                [mainTableView endUpdates];
            } else {
                [mainTableView reloadData];
            }
        });

The code for the method is in turn triggered by a method in Manager like so:

- (void) deleteMessagesForNotificationObjects: (NSArray <Object *> *) objects {

    // this is where the property that includes the NSIndexPath
    [self p_updatePathsToDeleteForDeletedObjects:objects];

    // this is the notification that should trigger the code above
    [[NSNotificationCenter defaultCenter] postNotificationName:@"RefreshTableView" object:self];

    // this is where I modify the underlying data structures that power
    // the table view's data source
    NSMutableArray *localObjects = [objects mutableCopy];

    for (Object *obj in localObjects) {
        [self deleteMessageWithToken:obj.token andUniqueID:nil andFireDate:obj.displayDate];
    }

    NSArray *allKeys = [self.displayDict allKeys];
    NSMutableArray <NSString *> *keysToDelete = [NSMutableArray new];
    for (NSString *key in allKeys) {
        NSMutableArray <Object *> *currentArr = self.displayDict[key];

        NSMutableArray <Object *> *objsToDelete = [NSMutableArray new];
        for (int i = 0; i < [localObjects count]; i ++) {
            if ([currentArr containsObject:localObjects[i]]) {
                [objsToDelete addObject:localObjects[i]];
            }
        }
        [currentArr removeObjectsInArray:objsToDelete];
        [localObjects removeObjectsInArray:objsToDelete];
        if ([currentArr count] < 1) {
            [keysToDelete addObject:key];
        }
    }

    [self.displayDict removeObjectsForKeys:keysToDelete];
    self.keyOrder = [[[self class] orderedKeysFromDict:self.displayDict] mutableCopy];

}

I am unclear as to what has to happen in what order. How do the commands indicating to a table view that it has to delete certain rows in an animated fashion (discussed here: Add/Delete UITableViewCell with animation?) relate to the ordering of actually modifying the underlying data source? In what order do I (1) animate row deletion and section deletion and (2) actually delete those rows and sections?

Community
  • 1
  • 1
helloB
  • 3,472
  • 10
  • 40
  • 87

1 Answers1

0

The answer is that the data source has to be modified inside beginUpdates and endUpdates, not in another method or elsewhere in the code.

helloB
  • 3,472
  • 10
  • 40
  • 87
  • This is incorrect. You *can* modify the datasource between begin and end updates, but it isn't required. The only thing required is that you modify the datasource *before* calling any of the table view update methods (like insert, delete etc). See, for example, listing 7-8 in this apple doc...https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html – danh Jan 09 '17 at 20:30
  • There's evidence in your question that you're doing work off the main. Maybe your real issue is that you're not executing things in the order you think. – danh Jan 09 '17 at 20:32