UITableView删除/添加行导致CoreData:严重的应用程序错误,如果在SplitViewController的MasterView中select了另一个对象

更新18/3#2。 我已经开始计算beginUpdates和EndUpdates,以确保它们是均匀的。 在出现exception之前,它们不同步。 不知道为什么。

更新18/3:我想我已经发现了这个问题 ,但我不确定是否知道如何解决这个问题。 经过几个小时的试验后,我发现我只能在该会话期间在svc的master tableview中select多个项目时才能使应用程序崩溃。 当在主表格视图中select另一个项目时,详细表格视图会获得一个新的对象集合,并且即使在更新/移动/删除/插入循环的中途,也会调用刷新表格。 因此,这个问题; 即使在detailviewcontroller上设置了新的故事对象,我认为它也有更新旧故事对象的说明。

在详细视图中设置新故事之前,如何获取animation/ coredata / tableview更新以完全处理? setEditing == NO时保存对managedObjectContext的更改。 我猜我需要做一个自定义的setStory setter处理所有的更新到UITableView / CoreData集在接受新的对象之前?

这段代码在didSelectRowAtIndexPath的svc的主表控制器上被调用:

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

我尝试删除动作不会animation的行,并且应用程序本质上挂起以下错误(虽然从CoreData集中删除行)有间歇性错误。 如果我在一个会话中从svc中的主表控制器中select了多行,就会发生这种情况。

一些谷歌search后,我认为这可能是(无效)控制器的问题:(NSFetchedResultsController *)控制器didChangeObject:(id)更新后调用animation更改用户已作出。

我怎样才能解决这些间歇性的错误?

  • 16/3我试图真正简化我的代码。 我已经删除了所有对Managed Object Context的调用,并把它们放在setEditing中,删除了多余的[self.tableview reloadData]和[self.tableview setneedsdisplay],并使'reordering'bool完全失效(它仍然在代码中,但它总是设置为NO,所以没有区别)。 UITableView比以前更稳定,但是我仍然设法在删除(偶尔添加)时得到间歇性的错误 – 似乎需要一段时间才能崩溃,但它仍然会。
  • 15/3:我认为这与CoreData / UITableView不同步有关 – CoreData认为它比UITableViewless或多
  • CoreData集没有问题,它只是事物的animation/ UI方面(事情被删除)
  • 这是间歇性的,只是在一些删除
  • 在从railparade的一些帮助后,我实现了NSFetchedResultsChangeMove在didChangeObject:它修复了移动错误,但没有删除错误。

任何人都可以看到我错过的任何东西,或者我可以检查吗? 如果能帮助解决问题,很乐意提供更多的信息。

抱歉在这里张贴的淫秽数量的代码。

错误:

CoreData:错误:严重的应用程序错误。 在调用-controllerDidChangeContent:期间,NSFetchedResultsController的委托捕获到exception。 尝试将第3行插入到第0部分中,但用userInfo(null)更新后,第0部分中只有3行

 // // 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's 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's 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's not set on the iPhone. [addButtonItem release]; [playButtonItem release]; } [story release]; [fetchedResultsController release]; [super dealloc]; } @end 

更新18/3#3我读了这个问题,关于一个fetchedResultsController需要将其委托设置为零,然后创build一个新的。 我放

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

在didselectrowatindexpath的SVC主视图中,问题已经停止。 每当在主视图中select一行时,RefreshTables就会创build一个新的fetchedResultsController,我认为即使select了一个新的故事对象,它们仍然会产生干扰。

你错过了在didChangeObject中实现NSFetchedResultsChangeMove:

这是我的代码,它做同样的事情:

 - (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]; } }