I have an UITableViewController
that shows a list using NSFetchedResultsController
.
This is a list of myObject and has a attribute date
.
I have created a method - (NSFetchedResultsController *)fetchedResultsController
to get all Entries sorted by date.
The result is this:
After this I have added the - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
so that time + date would be shown in dateStyle:NSDateFormatterMediumStyle
.
As shown below, you can see that the dates are printed properly, but the 2 lines with MAR 12, 2014 and the other 2 with MAR 14, 2014 are not grouped together.
My guess is that I need to change the numberOfSectionsInTableView
method, as it is still looking at the dates + time, and not only to the dates, but I have no idea how.
My code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSInteger count = [[self.fetchedResultsController sections] count];
return count;
}
One possibility is to add an attribute to my Object like 'sectionIdentifier' to store only dates in it and use that. I would like to know if anyone has another idea how to approach this. Is it possible to get these dates grouped together my adding code to - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
?
I like controlling it with the FRC, and leave the delegate calls to change it if they want different/custom functionality. I also believe that the FRC should generally be constructed as a class method on the entity being observed.
You can make the formatted string be a full-on attribute of the object, but that seems wasteful.
You could also make it be a true transient attribute. That may be more proper, but I was using an existing model, and didn't want to change the model. Since that project, I have just kept with this methodology for any other similar cases.
Anyway, this example is almost taken straight from an existing app. I had to change some things, so I hope I didn't leave out anything.
Create the fetch request used by your FRC, so that it sorts based on the date
property of the object. This allows normal core data fetching on a standard property.
+ (NSFetchedResultsController *)
fetchedResultsController:(NSManagedObjectContext *)context
{
NSFetchRequest* request = [NSFetchRequest
fetchRequestWithEntityName:[self entityName]];
request.sortDescriptors = @[[NSSortDescriptor
sortDescriptorWithKey:@"date"
ascending:YES]];
request.fetchBatchSize = 30;
request.returnsObjectsAsFaults = NO;
NSString *cacheName = [[self entityName]
stringByAppendingString:@"-all-bydate"];
return [[NSFetchedResultsController alloc]
initWithFetchRequest:request
managedObjectContext:context
sectionNameKeyPath:@"dateAsSectionName"
cacheName:cacheName];
}
Add an instance variable to your subclass... you will manage it yourself...
@implementation MyManagedObjectSubclass {
NSString *dateAsSectionName_;
}
Add an accessor method. We only create the formatter once. Change it to your own liking.
- (NSString*)dateAsSectionName
{
static NSDateFormatter *dateFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = @"EEE, MMM d, h:mm a";
dateFormatter.dateFormat = @"EEEE MMMM d";
});
if (dateAsSectionName_ == nil) {
NSDate *date = [self primitiveDate];
dateAsSectionName_ = [dateFormatter stringFromDate:date];
}
return dateAsSectionName_;
}
Clear out our section name when the date changes, so we will re-compute its value the next time the accessor is called.
- (void)setDate:(NSDate*)date
{
if ([[self primitiveDate] isEqualToDate:date]) return;
[self willChangeValueForKey:@"date"];
[self setPrimitiveDate:date];
[self didChangeValueForKey:@"date"];
dateAsSectionName_ = nil;
}
Tell KVO that dateAsSectionName
depends on date
.
+ (NSSet *)keyPathsForValuesAffectingDateAsSectionName
{
return [NSSet setWithObject:@"date"];
}