A NSFetchedResultsController with date as sectionNameKeyPath
I develop an application which uses Core Data. In one UITableView, I want to display a list of my entities, sorted by the saved 开发者_如何学Pythondate of the objects. When I do this:
fetchedResultsController = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext
sectionNameKeyPath:@"date"
cacheName:nil];
I get for each object a new section because this code groups the dates according to the seconds, too. But I want a list of the objects, grouped by date, but only according to the day, month and year. Is it possible and how?
Thank you very much for your help!! ;)
This should do the trick for you:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *rawDateStr = [[[self.fetchedResultsController sections] objectAtIndex:section] name];
// Convert rawDateStr string to NSDate...
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss ZZ"];
NSDate *date = [formatter dateFromString:rawDateStr];
// Convert NSDate to format we want...
[formatter setDateFormat:@"d MMMM yyyy"];
NSString *formattedDateStr = [formatter stringFromDate:date];
return formattedDateStr;
}
[EDIT]
Jus saw your comment and for what you are trying to achieve, you could create a transient NSDate
attribute (non persistent) that is formatted in a similar way to the above code (i.e. without H:mm:ss ZZZZ) and use that attribute as your sectionNameKeyPath
value.
So in a nutshell for a foo
object, with fooDate
and fooDateTransient
attributes, you would:
Get your
foo.fooDate
attributeTransform it using the code above (or similar) and assign the
NSDate
result tofoo.fooDateTransient
Use
fooDateTransient
as yoursectionNameKeyPath
when creating thefetchedResultsController
object.
PS: I haven't tested this myself but should be worth a shot!
Good luck, Rog
Check out: http://developer.apple.com/library/ios/#samplecode/DateSectionTitles/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009939
It works with month and year, but it's quite easy to make it work with day, month and year.
The following is a Swift 3 solution to sort by date but have section titles corresponding to individual days.
- Add a transient property called
daySectionIdentifier
to your entity in Core Data. - Regenerate your
NSManagedObject
subclass. Delete the property fordaySectionIdentifier
that may get generated inEntity+CoreDataProperties.swift
. To the
Entity+CoreDataClass.swift
file, add the following getter fordaySectionIdentifier
:// Transient property for grouping a table into sections based // on day of entity's date. Allows an NSFetchedResultsController // to sort by date, but also display the day as the section title. // - Constructs a string of format "YYYYMMDD", where YYYY is the year, // MM is the month, and DD is the day (all integers). public var daySectionIdentifier: String? { let currentCalendar = Calendar.current self.willAccessValue(forKey: "daySectionIdentifier") var sectionIdentifier = "" if let date = self.date as? Date { let day = currentCalendar.component(.day, from: date) let month = currentCalendar.component(.month, from: date) let year = currentCalendar.component(.year, from: date) // Construct integer from year, month, day. Convert to string. sectionIdentifier = "\(year * 10000 + month * 100 + day)" } self.didAccessValue(forKey: "daySectionIdentifier") return sectionIdentfier }
In your
UITableViewController
implementation, add the following method:override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { var sectionTitle: String? if let sectionIdentifier = fetchedResultsController.sections?[section].name { if let numericSection = Int(sectionIdentifier) { // Parse the numericSection into its year/month/day components. let year = numericSection / 10000 let month = (numericSection / 100) % 100 let day = numericSection % 100 // Reconstruct the date from these components. var components = DateComponents() components.calendar = Calendar.current components.day = day components.month = month components.year = year // Set the section title with this date if let date = components.date { sectionTitle = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .none) } } } return sectionTitle }
- When constructing your
NSFetchedResultsController
, call the initializer with"daySectionIdentifier"
as thesectionNameKeyPath
parameter. - Set your
NSFetchedResultsController
's sort descriptor to your entity's plain old"date"
attribute. Importantly, the sort order based on"date"
will be consistent with the sort order based on the section identifier that we just constructed.
You should now have your table view grouped into sections by day (e.g., "Feb 6, 2017"), and sorted by fine-grained date.
I think this would be better.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
// Replace DataClassObject with whatever object your using
DataClassObject *tempObject = [[sectionInfo objects] objectAtIndex:0];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"d MMMM yyyy"];
NSString *formattedDateStr = [formatter stringFromDate:tempObject.date];
[dateFormatter release]
return formattedDateStr;
}
I used @BoltClock's a Unicorn and @Rog's anwser when having the same issue. Simply added a transient NSString *sectionTitle to my managed object, used @"sectionTitle" as sectionNameKeyPath and created a custom getter like so:
-(NSString *)sectionTitle
{
NSDate *_now = [NSDate date];
NSDate *_today = [_now dateByAddingTimeInterval: -86400.0];
NSDate *_yesterday = [_now dateByAddingTimeInterval: -172800.0];
NSDate *_thisWeek = [_now dateByAddingTimeInterval: -604800.0];
NSDate *_lastWeek = [_now dateByAddingTimeInterval: -1209600.0];
NSDate *_thisMonth = [_now dateByAddingTimeInterval: -2629743.0];
// if better precision required use something more sophisticated for month...
double today = [_today timeIntervalSince1970];
double yesterday = [_yesterday timeIntervalSince1970];
double thisWeek = [_thisWeek timeIntervalSince1970];
double lastWeek = [_lastWeek timeIntervalSince1970];
double thisMonth = [_thisMonth timeIntervalSince1970];
[self willAccessValueForKey:@"timestamp"];
double ts = [self.timestamp timeIntervalSince1970];
[self didAccessValueForKey:@"timestamp"];
NSString *title = @"";
if(ts >= today) title = NSLocalizedString(@"TODAY",nil);
else if (ts >= yesterday) title = NSLocalizedString(@"YESTERDAY",nil);
else if (ts >= thisWeek) title = NSLocalizedString(@"THIS WEEK",nil);
else if (ts >= lastWeek) title = NSLocalizedString(@"LAST WEEK",nil);
else if (ts >= thisMonth) title = NSLocalizedString(@"THIS MONTH",nil);
return title;
}
精彩评论