UITableView delete / add row calls CoreData: a serious application error if another object was selected in the MasterView for SplitViewController

Update 18/3 # 2. I started counting beginUpdates and EndUpdates to make sure they are even. Right before this exception, they go out of sync. Not sure why.

UPDATE 18/3: I think I found the problem , but I'm not sure if I know how to fix it. After experimenting for several hours, I found that I can only crash the application when more than one item in the main view of the svc table was selected in this session. When another item is selected in the main table view, the detail table view gets a new set of objects, and refreshtables is called EVEN if it is halfway through the update / move / delete / insert cycle. Hence the problem; I think he has instructions for updating the old story object, even if the new story object was installed on the detailviewcontroller.

How can I fully process animation updates / coredata / tableview before a new plot is set in the detail view? I save changes to managedObjectContext when setEditting == NO. I assume that I need to create a custom setStory setter that processes all updates in a UITableView / CoreData before accepting a new object?

This code is called in the main controller of the svc table in the didSelectRowAtIndexPath file:

[detailViewController setStory:storySet]; //where storySet is the new story object [detailViewController refreshTables]; 

I have intermittent errors when I try to delete a line in which the action will not be animated, and the application essentially freezes with the following error (the line is deleted from the CoreData set, though). This happens if I select multiple rows from the main table controller in svc in one session.

After some googling, I thought it might be a problem with the controller (void): (NSFetchedResultsController *) controller didChangeObject: (id) , which is called after the update to revive the changes made by the user,

How can I fix these intermittent errors?

  • 16/3 I tried to really simplify my code. I deleted all the calls in the Context of the managed object and placed them in setEditing, deleted the unnecessary [self.tableview reloadData] and [self.tableview setneedsdisplay] and completely deprived the “reordering” bool (it is still in the code, but it always set to NO, so it doesn’t matter). The UITableView is more stable than ever, but I still manage to get intermittent errors when deleting (and sometimes when adding) - it seems like it takes some time for the crash, but it still will. **
  • 15/3: I think this is due to the fact that CoreData / UITableView is out of sync - CoreData thinks it has less or more than UITableView
  • No problem with a set of CoreData, it's just the animation / user interface side (things are deleted)
  • It is intermittent, only with some deletions
  • After some help from railparade, I implemented NSFetchedResultsChangeMove in the didChangeObject file: the move error was fixed, but not the delete error.

Can anyone see anything that I missed, or that I can check? I am glad to provide additional information if this helps to solve the problem.

Sorry for the indecent amount of code posted here.

Error:

CoreData: Error: A serious application error. Ruled out. from the NSFetchedResultsController delegate during a call to -controllerDidChangeContent :. try inserting line 3 into section 0, but there are only 3 lines in section 0 after updating with userInfo (Zero)

 // // MakeSentenceTableViewController.h // StoryBot // // Created by Glen Storey on 25/10/10. // Copyright 2010 Glen Storey. All rights reserved. // #import <UIKit/UIKit.h> #import "AddStoryItem.h" #import "Story.h" #import "Sentence.h" @interface MakeSentenceTableViewController : UITableViewController <NSFetchedResultsControllerDelegate, AddStoryItemDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate, UIPopoverControllerDelegate> { NSManagedObjectContext *managedObjectContext; NSFetchedResultsController *fetchedResultsController; UIPopoverController *popoverController; UIBarButtonItem *playButtonItem; UIBarButtonItem *addButtonItem; BOOL reordering; BOOL insert; BOOL delete; BOOL move; int beginUpdatesCount; int endUpdatesCount; } @property (nonatomic, retain) Story *story; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain) UIBarButtonItem *playButtonItem; @property (nonatomic, retain) UIBarButtonItem *addButtonItem; @property BOOL reordering, insert, delete, move; -(IBAction)createStoryModal:(id)sender; -(void)refreshTables; -(IBAction)pushShare:(id)sender; @end // // MakeSentenceTableViewController.m // // // Created by Glen Storey on 25/10/10. // Copyright 2010 Glen Storey. All rights reserved. // #import "MakeSentenceTableViewController.h" #import "ShareViewController.h" #import "StoryBotAppDelegate.h" @implementation MakeSentenceTableViewController @synthesize story, managedObjectContext, addButtonItem, playButtonItem, reordering, insert, delete, move; -(void)addStoryItemAction:(NSString*)text order:(NSNumber*)order image:(NSString*)image thumb:(NSString*)thumb{ NSLog(@"Text: %@, Order: %@, Image: %@.", text, order, image); NSLog(@"beginUpdatesCount: %d vs. endUpdatescount: %d", beginUpdatesCount, endUpdatesCount); NSSet *sentences = [story sentences]; NSNumber *maxOrder = [sentences valueForKeyPath:@"@max.order"]; NSLog(@"maxOrder: %@", maxOrder); if(maxOrder == 0){ maxOrder = [[NSNumber alloc] initWithInteger: 0]; } //make a new sentence! Sentence *sentence = [NSEntityDescription insertNewObjectForEntityForName:@"Sentence" inManagedObjectContext:managedObjectContext]; [sentence setText: text]; [sentence setImage: image]; [sentence setThumb: thumb]; [sentence setBelongsTo: story]; if([maxOrder intValue] >= 1 ){ [sentence setOrder: [[NSNumber alloc] initWithInteger:[maxOrder intValue]+1]]; }else{ [sentence setOrder: [[NSNumber alloc] initWithInteger:1]]; } NSMutableSet *mutableSet = [[NSMutableSet alloc] initWithSet:sentences]; [mutableSet addObject:sentence]; //NSLog(@"sentences before setWithSet %@", mutableSet); sentences = [[NSSet alloc] initWithSet: mutableSet]; //NSLog(@"sentences after setWithSet %@", sentences); [story setSentences:sentences]; //NSError *error; //BOOL isSaved = [managedObjectContext save:&error]; //NSLog(@"isSaved? %@", (isSaved ? @"YES" :@"NO ") ); //if (!isSaved) { //NSLog(@"%@:%s Error saving context: %@", [self class], _cmd, [error localizedDescription]); //Don't worry about this warning - just rem it out when finished (just a log) // return; //} [sentences release]; [mutableSet release]; //[self.tableView reloadData]; //[self.tableView setNeedsDisplay]; [self dismissModalViewControllerAnimated:YES]; } #pragma mark - #pragma mark View lifecycle -(id)initWithNibName:(NSString*)name bundle:(NSBundle*)bundle; { self = [super initWithNibName:name bundle:bundle]; if (self) { self.title = @"My Stories"; addButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(createStoryModal:)]; playButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(pushShare:)]; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { [addButtonItem setEnabled:NO]; [playButtonItem setEnabled:NO]; } NSArray* toolbarItems = [NSArray arrayWithObjects: addButtonItem, [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], playButtonItem, nil]; [toolbarItems makeObjectsPerformSelector:@selector(release)]; self.toolbarItems = toolbarItems; //NSLog(@"self: %@ self.toolbarItems: %@", self, self.toolbarItems); //Do I need to release UIBarButtonItems? NSLog(@"initWithNibName:"); } return self; } -(void)refreshTables{ //use this to refresh for new table content. Also calls self.tableview reloadData so you don't need to call it afterward. NSLog(@"===================================refreshTables"); NSLog(@"story object %@", story); if (managedObjectContext == nil) { NSLog(@"managedObjectContext == nil"); managedObjectContext = [(StoryBotAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; NSLog(@"After managedObjectContext: %@", managedObjectContext); }else{ NSLog(@"managedObjectContext != nil"); } NSFetchRequest *request = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Sentence" inManagedObjectContext:managedObjectContext]; [request setEntity:entity]; //sorting stuff: NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"order" ascending: YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil]; [request setSortDescriptors:sortDescriptors]; [sortDescriptors release]; [sortDescriptor release]; NSPredicate *predicateTitle = [NSPredicate predicateWithFormat:@"belongsTo=%@",story]; [request setPredicate :predicateTitle]; NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease]; [dateFormatter setDateFormat:@"EEE, d MMM yyyy HH:mm:ss"]; NSString *dateString = [dateFormatter stringFromDate:[story creationDate]]; NSLog(@"dateString: %@", dateString); fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:dateString]; fetchedResultsController.delegate = self; [request release]; NSError *error; [fetchedResultsController performFetch:&error]; [self.tableView reloadData]; } - (void)viewDidLoad { [super viewDidLoad]; self.title = @"My Story"; NSLog(@"Passed Story Object: %@", story); //NSLog(@"managedObjectContext: %@", managedObjectContext); //Need a predicate for belongsTo here. self.tableView.rowHeight = 50; if(story != NULL){ if (managedObjectContext == nil) { NSLog(@"managedObjectContext == nil"); managedObjectContext = [(StoryBotAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext]; NSLog(@"After managedObjectContext: %@", managedObjectContext); }else{ NSLog(@"managedObjectContext != nil"); } NSFetchRequest *request = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"Sentence" inManagedObjectContext:managedObjectContext]; [request setEntity:entity]; //sorting stuff: NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"order" ascending: YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects: sortDescriptor, nil]; [request setSortDescriptors:sortDescriptors]; [sortDescriptors release]; [sortDescriptor release]; NSPredicate *predicateTitle = [NSPredicate predicateWithFormat:@"belongsTo=%@",story]; [request setPredicate :predicateTitle]; fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:nil cacheName:nil]; fetchedResultsController.delegate = self; [request release]; NSError *error; [fetchedResultsController performFetch:&error]; } #pragma mark - #pragma mark Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { //Return the number of rows in the section. NSArray *sections = [fetchedResultsController sections]; NSInteger count = 0; if ([sections count]){ id <NSFetchedResultsSectionInfo> sectionInfo = [sections objectAtIndex:section]; count = [sectionInfo numberOfObjects]; } NSLog(@"# of rows in section %d", count); return count; } // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } // Configure the cell... Sentence *sentenceAtCell = [fetchedResultsController objectAtIndexPath:indexPath]; //NSLog(@"sentenceAtCell: %@", sentenceAtCell); cell.textLabel.text = [sentenceAtCell text]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *uniquePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[sentenceAtCell thumb]]; // This should crop it as you want - you've just got to create cropRect. UIImage *largeImage = [UIImage imageWithContentsOfFile: uniquePath]; CGRect cropRect = CGRectMake(0, 0, 66, 50); /*if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { if ([[UIScreen mainScreen] scale] == 2) { // running an iPhone 4 or equiv. res device. cropRect = CGRectMake(15, 14, 100, 75); } }*/ CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect); cell.imageView.image = [UIImage imageWithCGImage: imageRef]; CGImageRelease(imageRef); //NSLog(@"indexPath: %@", indexPath); return cell; } // Override to support editing the table view. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { NSLog(@"Delete row"); NSFileManager *fileManager = [NSFileManager defaultManager]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectoryPath = [paths objectAtIndex:0]; // 1. Look at the sentence we're about to delete. Sentence *sentenceRetire = [fetchedResultsController objectAtIndexPath:indexPath]; // 2. Does it have an order of 0? NSLog(@"=================== sentenceRetire: %@", sentenceRetire); if([[sentenceRetire order] intValue] == 0){ // YES: Is there another sentence in this story? Story *storyRetire = [sentenceRetire belongsTo]; NSSet *sentencesInRetiredSentenceSet = [storyRetire sentences]; if ([sentencesInRetiredSentenceSet count] > 1){ // YES: Set the sentence with the smallest order to an order of 0 // then delete the sentence + files NSPredicate *predicateTitle = [NSPredicate predicateWithFormat:@"order>0"]; NSSet *sentencesWithPotentialToBeTitle = [sentencesInRetiredSentenceSet filteredSetUsingPredicate:predicateTitle]; NSNumber *minOrder = [sentencesWithPotentialToBeTitle valueForKeyPath:@"@min.order"]; NSPredicate *predicateOrder = [NSPredicate predicateWithFormat:@"order=%d",[minOrder intValue]]; NSSet *sentenceWithPotentialToBeTitle = [sentencesWithPotentialToBeTitle filteredSetUsingPredicate:predicateOrder]; //note the sentence (singular) not sentences. This is the sentence who order needs to be updated. NSLog(@"setenceWithPotentialToBeTitle (check order isn't null on crash): %@", sentenceWithPotentialToBeTitle); Sentence *sentenceToPromote = [sentenceWithPotentialToBeTitle anyObject]; //now we know which sentence to promote we can delete the sentence & Files. [managedObjectContext deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]]; NSString *imageTrash = [documentsDirectoryPath stringByAppendingPathComponent:(NSString*)[sentenceRetire image]]; NSString *thumbTrash = [documentsDirectoryPath stringByAppendingPathComponent:[sentenceRetire thumb]]; NSLog(@"About to delete these files: %@, %@", imageTrash, thumbTrash); [fileManager removeItemAtPath:imageTrash error:NULL]; [fileManager removeItemAtPath:thumbTrash error:NULL]; //and promote the new title. [sentenceToPromote setOrder:[[NSNumber alloc] initWithInteger:0]]; }else{ // NO: We're deleting this story // Delete the files! [managedObjectContext deleteObject:storyRetire]; NSString *imageTrash = [documentsDirectoryPath stringByAppendingPathComponent:(NSString*)[sentenceRetire image]]; NSString *thumbTrash = [documentsDirectoryPath stringByAppendingPathComponent:[sentenceRetire thumb]]; //NSLog(@"About to delete these files: %@, %@", imageTrash, thumbTrash); [fileManager removeItemAtPath:imageTrash error:NULL]; [fileManager removeItemAtPath:thumbTrash error:NULL]; //Pop back. if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { NSLog(@"last sentence in story - delete that story and point, somewhere!"); //probably delete the sentece to clear the table, then delete the story using storyRetire } else{ [self.navigationController popViewControllerAnimated:YES]; } } }else{ // NO: Delete the Sentence Object. [managedObjectContext deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]]; NSString *imageTrash = [documentsDirectoryPath stringByAppendingPathComponent:(NSString*)[sentenceRetire image]]; NSString *thumbTrash = [documentsDirectoryPath stringByAppendingPathComponent:[sentenceRetire thumb]]; //NSLog(@"About to delete these files: %@, %@", imageTrash, thumbTrash); [fileManager removeItemAtPath:imageTrash error:NULL]; [fileManager removeItemAtPath:thumbTrash error:NULL]; } // Save the context. //NSError *error; //if (![managedObjectContext save:&error]) { /* Replace this implementation with code to handle the error appropriately. abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. If it is not possible to recover from the error, display an alert panel that instructs the user to quit the application by pressing the Home button. */ // NSLog(@"Unresolved error %@, %@", error, [error userInfo]); // abort(); //} } } - (void)setEditing:(BOOL)editing animated:(BOOL)animated { [super setEditing:editing animated:animated]; if (!editing) { //save here NSError *error; BOOL isSaved = [managedObjectContext save:&error]; NSLog(@"isSaved? %@ ======================================", (isSaved ? @"YES" :@"NO ") ); } } // Override to support rearranging the table view. - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { //this implementation is from here: http://www.cimgf.com/2010/06/05/re-ordering-nsfetchedresultscontroller/ NSMutableArray *things = [[fetchedResultsController fetchedObjects] mutableCopy]; // Grab the item we're moving. NSManagedObject *thing = [fetchedResultsController objectAtIndexPath:fromIndexPath]; // Remove the object we're moving from the array. [things removeObject:thing]; // Now re-insert it at the destination. [things insertObject:thing atIndex:[toIndexPath row]]; // All of the objects are now in their correct order. Update each // object displayOrder field by iterating through the array. int i = 0; for (NSManagedObject *mo in things) { [mo setValue:[NSNumber numberWithInt:i++] forKey:@"order"]; } NSLog(@"things: %@", things); [things release], things = nil; //reordering = YES; //NSLog(@"moveRowAtIndexPath: IS reordering"); //NSError *error; //[managedObjectContext save:&error]; } #pragma mark - #pragma mark Table view delegate /** Delegate methods of NSFetchedResultsController to respond to additions, removals and so on. */ - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { // The fetch controller is about to start sending change notifications, so prepare the table view for updates. NSLog(@"controllerWillChangeContent: BeginUpdates"); [self.tableView beginUpdates]; beginUpdatesCount++; NSLog(@"====================beginUpdates was just incremented"); NSLog(@"beginUpdatesCount %d", beginUpdatesCount); NSLog(@"endUpdatesCount %d", endUpdatesCount); } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { NSLog(@"didChangeObject: %@", anObject); UITableView *tableView; tableView = self.tableView; switch(type) { case NSFetchedResultsChangeInsert: NSLog(@"ResultsChangeInsert:"); insert = YES; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: NSLog(@"ResultsChangeDelete:"); delete = YES; [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeMove: NSLog(@"ResultsChangeMove:"); move = YES; [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationNone]; break; default: NSLog(@"switch problem - default"); } } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { NSLog(@"didChangeSection:"); switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { NSLog(@"didChangeContent"); // The fetch controller has sent all current change notifications, so tell the table view to process all updates. NSLog(@"almost endUpdates=============================================="); if(delete){ NSLog(@"endUpdates delete"); delete = NO; } if(move){ NSLog(@"endUpdates move"); move = NO; } if(insert){ NSLog(@"endUpdates insert"); insert = NO; } [self.tableView endUpdates]; endUpdatesCount++; NSLog(@"====================endUpdates was just incremented"); NSLog(@"endUpdatesCount %d", endUpdatesCount); NSLog(@"beginUpdatesCount %d", beginUpdatesCount); NSLog(@"endUpdates finished"); } #pragma mark - #pragma mark Memory management - (void)dealloc { NSLog(@"Dealloc Sentence"); //[fliteEngine stopTalking]; //[fliteEngine release]; addButtonItem = nil; playButtonItem = nil; if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { //It doesn't seem to get allocated because it not set on the iPhone. [addButtonItem release]; [playButtonItem release]; } [story release]; [fetchedResultsController release]; [super dealloc]; } @end 
+6
source share
2 answers

Update 18/3 # 3 I read this question about a fetchedResultsController device that needs to set its delegate to zero before creating a new one. I put

 detailViewController.fetchedResultsController = nil; detailViewController.fetchedResultsController.delegate = nil; 

in the main SVC view on didselectrowatindexpath, and the problem is stopped. RefreshTables created a new fetchedResultsController every time a row was selected in the main view, and I think they still intervened even when a new history object was selected.

+6
source

You missed the implementation of NSFetchedResultsChangeMove in the didChangeObject file:

Here is my code that does the same:

 - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { sectionHeadersChanged=NO; [self._tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { switch(type) { case NSFetchedResultsChangeInsert: [self._tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic]; break; case NSFetchedResultsChangeDelete: [self._tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } sectionHeadersChanged=YES; } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { UITableView *tableView = self._tableView; switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; break; case NSFetchedResultsChangeUpdate: [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationBottom]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self._tableView endUpdates]; //reload all sections if(sectionHeadersChanged==YES){ [self._tableView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self._tableView.numberOfSections)] withRowAnimation:UITableViewRowAnimationNone]; } } 
+3
source

Source: https://habr.com/ru/post/910388/


All Articles