I am using NSFetchedResultsController to update data in a table format. The data itself is provided through an XML parser that runs in the background. After the parser has finished, it saves the data in its context. NSFetchedResultsController immediately replaces these changes and starts calling the delegate method -(void)controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: for each updated item. It is also fast and looks normal in log files.
However, in -(void)controllerDidChangeContent: I call UITableView -(void)endUpdates . Then I see the update animation on the screen, but in all the cells next to the last one, which is only half visible, the only thing that is visible is the image on the left side of the cell. All text labels are not visible. It takes 5 to 10 seconds, then all the shortcuts are displayed.
However, if I ignore all calls to the NSFetchedResultsController delegate and just call [self.tableView reloadData] on -(void)controllerDidChangeContent: everything works without problems. Content is right there.
Does anyone know what I'm doing wrong here? The profiler shows that the main thread basically does nothing. Touch events are handled properly, except for events sent to the table view. They are not processed. It seems that the table view is busy with some serious work, but I really don't know what it could be, since the animation has already been done.
Here is my implementation of NSFetchedResultsControllerDelegate :
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { NSLog(@"%s", __PRETTY_FUNCTION__); [self.tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { NSLog(@"%s", __PRETTY_FUNCTION__); switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath*)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath*)newIndexPath { NSLog(@"%s", __PRETTY_FUNCTION__); 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: [(NewsItemCell*)[tableView cellForRowAtIndexPath:indexPath] updateWithNews:[self.fetchedResultsController objectAtIndexPath:indexPath]]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; break; } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { NSLog(@"%s", __PRETTY_FUNCTION__); [self.tableView endUpdates]; }
And this is the code for my cell location:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return self.fetchedResultsController.sections.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id<NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController.sections objectAtIndex:section]; return sectionInfo.numberOfObjects; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { News* model = [self.fetchedResultsController objectAtIndexPath:indexPath]; NewsItemCell* cell = (NewsItemCell*)[tableView dequeueReusableCellWithIdentifier:NewsCellReuseIdentifier]; [cell updateWithNews:model]; cell.accessoryType = (model.content ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone); return cell; }
And a pretty basic cell update:
- (void)updateWithNews:(News*)news { NSString* dateString = [[NSDateFormatter outputDateFormatter] stringFromDate:news.date]; self.headlineLabel.text = (news.headline ? news.headline : NSLocalizedString(@"<NewsNoHeadlineReplacement>", nil)); self.metaInfoLabel.text = [NSString stringWithFormat:NSLocalizedString(@"<NewsMetaInfoFormatDate>", nil), (dateString ? dateString : (NSLocalizedString(@"<NewsNoDateReplacement>", nil)))]; self.readIndicatorView.hidden = (news.read != nil && [news.read compare:news.parsingDate] == NSOrderedDescending); }
Placeholders are also not shown. Labels are completely empty. Only the image is visible!