UPDATE: In the comments someone pointed out that I was unnecessarily dispatching to the main thread. After removing the dispatches and unnecessary begin/end updates
, now when I try to delete a cell, it calls didChangeObject
with case NSFetchedResultsChangeUpdate
(as opposed to NSFetchedResultsChangeDelete
), which calls configureCell
.
The line that crashes the program is CollectedLeaf* theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];
in the below method. The crash log is, no section at index 20 in sections list
.
- (void)configureCell:(SpeciesCell *)cell atIndexPath:(NSIndexPath *)indexPath
{
CollectedLeaf* theCollectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];
[cell setCollectedLeaf:theCollectedLeaf];
}
I am getting an Invalid update: invalid number of rows in section
error every time I swipe to delete a cell from my table. I get the breakpoint specifically at [_table endUpdates]
in controllerDidChangeContent
.
In many SO posts, the reason for this error is that the object had not been deleted from data source before deleting the row from the table. I delete the object from the data source in commitEditingStyle
, which is called before deleteRowsAtIndexPaths
in didChangeObject
.
The fact that I'm still getting the Invalid update
despite my order leads me to think that I am not properly/successfully deleting it from the data source. I am still new to iOS - how can I make sure an object is removed from my core data?
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[_table beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath {
switch(type) {
case NSFetchedResultsChangeInsert:
[_table beginUpdates];
[_table insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
[_table endUpdates];
break;
case NSFetchedResultsChangeDelete:
NSLog(@"delete called");
[_table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
// [self configureCell:[_table cellForRowAtIndexPath:indexPath]
// atIndexPath:indexPath];
[_table reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
NSLog(@"fetched update");
break;
case NSFetchedResultsChangeMove:
[_table deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
[_table insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type
{
switch(type)
{
case NSFetchedResultsChangeInsert:
[_table insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[_table deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeMove:
break;
case NSFetchedResultsChangeUpdate:
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[_table endUpdates];
}
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(_segmentedControl.selectedSegmentIndex == 1) {
NSLog(@"index path at editingStyleForRowAtIndexPath %@", indexPath);
return UITableViewCellEditingStyleDelete;
}
return NULL;
}
- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
// [super tableView: tableView didEndEditingRowAtIndexPath:indexPath];
if(_segmentedControl.selectedSegmentIndex == 1) {
for (UITableViewCell *cell in [_table visibleCells]) {
for (UIView *subview in cell.contentView.subviews)
[subview.layer removeAllAnimations];
}
}
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
/*Only allow deletion for collection table */
if(_segmentedControl.selectedSegmentIndex == 1) {
if (editingStyle == UITableViewCellEditingStyleDelete)
{
NSLog(@"index path at commitediting style %@", indexPath);
CollectedLeaf* collectedLeaf = [collectionFetchedResultsController objectAtIndexPath:indexPath];
LeafletPhotoUploader * leafletPhotoUploader = [[LeafletPhotoUploader alloc] init];
leafletPhotoUploader.collectedLeaf = collectedLeaf;
if([LeafletUserRegistration isUserRegistered]) {
[leafletPhotoUploader deleteCollectedLeaf:collectedLeaf delegate:self];
}
// Delete the managed object for the given index path
NSManagedObjectContext *context = [collectionFetchedResultsController managedObjectContext];
[context deleteObject:[collectionFetchedResultsController objectAtIndexPath:indexPath]];
// Save the context.
NSError *error;
if (![context save:&error])
{
NSLog(@"Failed to save to data store: %@", [error localizedDescription]);
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0)
{
for(NSError* detailedError in detailedErrors)
{
NSLog(@" DetailedError: %@", [detailedError userInfo]);
}
}
else
{
NSLog(@" %@", [error userInfo]);
}
}
}
}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"speciesCell";
SpeciesCell* speciesCell = (SpeciesCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
[self configureCell:speciesCell atIndexPath:indexPath];
speciesCell.labelCheckMark.backgroundColor = [UIColor blackColor];
return speciesCell;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
id <NSFetchedResultsSectionInfo> sectionInfo = [[collectionFetchedResultsController sections] objectAtIndex:section];
if([sectionInfo numberOfObjects] == 0){
UILabel *noDataLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, tableView.bounds.size.width, tableView.bounds.size.height)];
noDataLabel.text = @"Press the Snap It! tab to start collecting!";
//Start your collection by tapping on the Snap It! tab.";
noDataLabel.textColor = [UIColor whiteColor];
noDataLabel.textAlignment = NSTextAlignmentCenter;
tableView.backgroundView = noDataLabel;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
}
return [sectionInfo numberOfObjects];
}