4

I've been working on creating a UITableView that has its content loaded from a NSUserDefaults object. Whenever I attempt to delete a cell/section, however, the program crashes and spits out the following:

*** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:1030
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections.  The number of sections contained in the table view after the update (2) must be equal to the number of sections contained in the table view before the update (3), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).

No matter how much I read up on this error and try to fix it, I've been stuck here for the past few hours. Here is my method that is called when the user taps the "delete" button:

 (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //[[[NSUserDefaults standardUserDefaults] objectForKey:@"rawArray"] removeObjectAtIndex:indexPath.row];
        //[[[NSUserDefaults standardUserDefaults] objectForKey:@"rawDates"] removeObjectAtIndex:indexPath.row];

        NSMutableArray *rawArray = [[NSUserDefaults standardUserDefaults] objectForKey:@"rawArray"];
        NSMutableArray *rawDates = [[NSUserDefaults standardUserDefaults] objectForKey:@"rawDates"];

        [rawArray removeObjectAtIndex:indexPath.row];
        [rawDates removeObjectAtIndex:indexPath.row];

        [[NSUserDefaults standardUserDefaults] setObject:rawArray forKey:@"rawArray"];
        [[NSUserDefaults standardUserDefaults] setObject:rawDates forKey:@"rawDates"];

        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
        [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationRight];

    }    
}

and here are the methods used to determine number of rows and cells:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return [[[NSUserDefaults standardUserDefaults] arrayForKey:@"rawDates"] count];
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{

    return [[[NSUserDefaults standardUserDefaults] arrayForKey:@"rawDates"] objectAtIndex:section];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 1;
}

The last method in there returns a one because, as of right now, I plan on only have one cell per each section. Thank you for taking the time to look over here, and I really hope you can help me out! :/

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
insanj
  • 151
  • 1
  • 12

1 Answers1

15

Check out the Batch Insertion, Deletion, and Reloading of Rows and Sections

Basically you need to put any calls of these method

- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;

- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation: (UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;

between

- (void)beginUpdates;
- (void)endUpdates;

e.g.

[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
[tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationRight];
[tableView endUpdates];

Your dataSource also needs to reflect the changes by the time endUpdates is called


Personally I like to make a category to give a block based wrapper around this to

  1. stop me forgetting to call endUpdates
  2. it allows indentation to look right / be automatic

Category on UITableView

@interface UITableView (PSAdditions)

- (void)ps_updates:(void (^)(void))updateBlock;

@end

@implementation UITableView (PSAdditions)

- (void)ps_updates:(void (^)(void))updateBlock;
{
    [self beginUpdates];
    if (updateBlock) { updateBlock(); }
    [self endUpdates];
}

Which would look like this

[tableView ps_updates:^{
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationRight];
    [tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationRight];
}];
Paul.s
  • 38,494
  • 5
  • 70
  • 88
  • Oh wow, that instantly did the trick. I can't believe how simple that was- I really should read up on all the developer documentation before diving in. I'll definitely look into making UITableView (PSAdditions) subclass as well, thanks for the suggestion. Thank you so much, you have no idea how much you just made my day. :) – insanj Apr 22 '12 at 17:15
  • Using beginUpdates and endUpdates has been working perfectly, but only for the first time I delete something. The second time around, I always get a crash with the message: `*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'`. I scoured around and found that the issue is most likely with my numberOfRowsInSection method, but I'm not sure how would I alter it, seeing as I only want 1 cell per section, and I don't keep track of things I delete (and I don't know how I would). :/ – insanj Apr 22 '12 at 17:28
  • Quick clarification that's not a subclass I showed, it is a 'category', if you give that a quick google you'll find loads of examples etc. for your second comment - you need to ensure that the tableView's data source reflects any changes made to the tableView. Your probably best of starting a new question with some more specific details if you need help with that – Paul.s Apr 22 '12 at 18:37
  • Okay, thank you again so much for the update and information, you've been a great help. – insanj Apr 22 '12 at 21:44