Thursday, November 12, 2009

One fix to NSFetchedResultsController Delegate Methods

I've been testing yesterday's post about NSFetchedResultsController. It works well under all scenarios I've tested with section name key paths. It currently causes some problems when you're not using a section name key path. That was remedied with a single line of code in the controller:didChangeObject:atIndexPath:forChangeType:newIndexPath:method.

I have updated the Navigation-Based Core Data Application project template with this change.

Here is the new version, with the change in bold:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate: {
NSString *sectionKeyPath = [controller sectionNameKeyPath];
if (sectionKeyPath == nil)
break;

NSManagedObject *changedObject = [controller objectAtIndexPath:indexPath];
NSArray *keyParts = [sectionKeyPath componentsSeparatedByString:@"."];
id currentKeyValue = [changedObject valueForKeyPath:sectionKeyPath];
for (int i = 0; i < [keyParts count] - 1; i++) {
NSString *onePart = [keyParts objectAtIndex:i];
changedObject = [changedObject valueForKey:onePart];
}

sectionKeyPath = [keyParts lastObject];
NSDictionary *committedValues = [changedObject committedValuesForKeys:nil];

if ([[committedValues valueForKeyPath:sectionKeyPath] isEqual:currentKeyValue])
break;

NSUInteger tableSectionCount = [self.tableView numberOfSections];
NSUInteger frcSectionCount = [[controller sections] count];
if (tableSectionCount != frcSectionCount) {
// Need to insert a section
NSArray *sections = controller.sections;
NSInteger newSectionLocation = -1;
for (id oneSection in sections) {
NSString *sectionName = [oneSection name];
if ([currentKeyValue isEqual:sectionName]) {
newSectionLocation = [sections indexOfObject:oneSection];
break;
}

}

if (newSectionLocation == -1)
return; // uh oh

if (!(newSectionLocation == 0 && tableSectionCount == 1))
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:newSectionLocation] withRowAnimation:UITableViewRowAnimationFade];
NSUInteger indices[2] = {newSectionLocation, 0};
newIndexPath = [[[NSIndexPath alloc] initWithIndexes:indices length:2] autorelease];
}

}

case NSFetchedResultsChangeMove:
if (newIndexPath != nil) {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView insertRowsAtIndexPaths: [NSArray arrayWithObject:newIndexPath]
withRowAnimation: UITableViewRowAnimationRight
]
;

}

else {
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[indexPath section]] withRowAnimation:UITableViewRowAnimationFade];
}

break;
default:
break;
}

}





7 comments:

Adam said...

Jeff, I've had a problem I'm wondeirng if you can replicate:

Let's say I could possibly have 3 sections: A,B,C

Initially, I only have one in B. Now, say I add one so that I now have a section in A, I get a crash.

However, If I add one to C first, then A, it works fine. I'm guessing this is because I'm trying to insert at index 0, when number of sections = 1 and the
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
is never tripped because of the
!(sectionIndex == 0 && [self.tableView numberOfSections] == 1)
clause.

Adam said...

Actually, as I wrote that I figured out how to make it work:

if (!(sectionIndex == 0 && [self.tableView numberOfSections] == 1 && [self.tableView numberOfRowsInSection:0] == 0))
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];

Just add this to the end of the if clause:

&& [self.tableView numberOfRowsInSection:0] == 0

Jeff LaMarche said...

Adam:

Good catch. I'll add it and repost.

Web Design Chennai said...

Good hoot. Keep it up will visit your blog again for more valuable information’s…
http://www.winwebsolutionz.com

Edwin said...

scrub m65 kamagra attorney lawyer body scrub field jacket lovegra marijuana attorney injury lawyer

SEO Services Consultants said...

Nice information, many thanks to the author. It is incomprehensible to me now, but in general, the usefulness and significance is overwhelming. Thanks again and good luck! Web Design Company

h4ns said...

What youre saying is completely true. I know that everybody must say the same thing, but I just think that you put it in a way that everyone can understand. I also love the images you put in here. They fit so well with what youre trying to say. Im sure youll reach so many people with what youve got to say.

Arsenal vs Huddersfield Town live streaming
Arsenal vs Huddersfield Town live streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Wolverhampton Wanderers vs Stoke City Live Streaming
Notts County vs Manchester City Live Streaming
Notts County vs Manchester City Live Streaming
Bologna vs AS Roma Live Streaming
Bologna vs AS Roma Live Streaming
Juventus vs Udinese Live Streaming
Juventus vs Udinese Live Streaming
Napoli vs Sampdoria Live Streaming
Napoli vs Sampdoria Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
Fulham vs Tottenham Hotspur Live Streaming
AS Monaco vs Marseille Live Streaming
AS Monaco vs Marseille Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Alajuelense vs Perez Zeledon Live Streaming
Technology News | News Today | Live Streaming TV Channels