Recently, I wanted to add section headers to an NSFetchedResultsController, so that I could have sections in the corresponding UICollectionView. The Apple Documentation is clear that you add a value to the sectionNameKeyPath
method when you are creating the controller. Setting the value to nil
will return a fetched results controller with only 1 section. Providing a value for the sectionNameKeyPath
will create one section per unique value of the key path.
I wanted to sort by date so that I would have one section for each day. My first attempt was to simply add the date
property of my object to the sectoinNameKeyPath.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[self managedObjectContext]
sectionNameKeyPath:@"sessionDate"
cacheName:@"Sessions"];
This allowed me to get values for the number of sections and titles for the sections.
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return [[[self fetchedResultsController] sections] count];
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
MTNFolderHeader *header = nil;
if (kind == UICollectionElementKindSectionHeader) {
header = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"Header" forIndexPath:indexPath];
id <NSFetchedResultsSectionInfo> sectionInfo = [[[self fetchedResultsController] sections] objectAtIndex:[indexPath section]];
[[header titleLabel] setText:[sectionInfo name]];
}
return header;
}
Some of this code is specific to my application, but I can get the count of the sections array and I can get the [sectionInfo name]
for the title of the header. If you are using a UITableView the header viewForHeaderInSection code would be simpler than for the collection view.
However, I store records by timestamp. So, when I first implemented my fetch I was getting one section per record because the timestamps are unique (they include data beyond the day).
In order to create the grouping the way I want them. I will need to add a property to my objects. A few important notes from the documentation. When creating a custom grouping, the custom grouping must either be included as the primary sort key or else it must not modify the sort order. This means that if I sort by date as my primary sort, I cannot try to group the records by the firstName
property. Also, the grouping key can either be an actual property of the object, a transient property or a method. In my case, since I just wanted to display the dates in a more human friendly format, I decided just to create a method on my objects.
Session.h
- (NSString *) sectionTitle;
Session.m
- (NSString*)sectionTitle {
NSString *dateFormatTemplate = [NSDateFormatter dateFormatFromTemplate:@"MMMM d" options:0 locale:[NSLocale currentLocale]];
[[self formatter] setDateFormat:dateFormatTemplate];
return [[self formatter] stringFromDate:[self date]];
}
Now I modify my fetched results controller to use the new method.
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[appD managedObjectContext]
sectionNameKeyPath:@"sectionTitle"
cacheName:@"Sessions"];
And now I am getting my sessions grouped by day!