I've got an issue and I'm wondering if it's possible to solve. I have a UITableView that uses Core Data and NSFetchedResultsController. A big part of my app is organizing things based on dates. Ideally, I'd like to divide the TableView into sections based off of 3 date ranges. These ranges would be between now and 2 days, between 2 days and 6 days, and 10 days and beyond. The user has a UIDatePicker, and when they enter a date it would be automatically put into one of these organized sections. Now I know how to easily divide the tableview into sections by each single date, but not how to do it so each section has a time range. Thanks to anyone that might be able to help out.
Just did this myself. The end result is that core data objects are sorted into sections, each one being 1 day wide. Multiple objects may be in one section.
//at a point where you define your fetched results controller
//add #"sectionTitle" as sectionNameKeyPath:
//...
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:context sectionNameKeyPath:#"sectionTitle" cacheName:#"CacheName"];
//....
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString* dateString = [[[self.sleepQualityController sections] objectAtIndex:section] name];
return dateString;
}
//add this to your managed object's header file:
#property(nonatomic,assign)NSString* sectionTitle;
//this goes into your managed object's implementation file
-(NSDate *)dateWithOutTime:(NSDate *)datDate
{
if( datDate == nil ) {
datDate = [NSDate date];
}
NSDateComponents* comps = [[NSCalendar currentCalendar] components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit fromDate:datDate];
return [[NSCalendar currentCalendar] dateFromComponents:comps];
}
-(NSString*)sectionTitle
{
NSDateFormatter* dateFormaterWeekdayMonthDay = [[NSDateFormatter alloc] init];
dateFormaterWeekdayMonthDay.dateStyle = NSDateFormatterLongStyle;
return [NSString stringWithFormat:#"%#", [dateFormaterWeekdayMonthDay stringFromDate:[self dateWithOutTime:self.date]] ];
}
I would explore using NSExpressions in the fetch request. It can be difficult to find good documentation on using SQL-like expressions with fetch requests; but you can also write your own block for the query to use, which is pretty killer.
Basically what you want is an NSExpression which returns a string for the section name, which you can tell the NSFetchedResultsController to use for the section name key path. You will need to build up an NSExpressionDescription around the expression to add it to the fetch request.
I hope this puts you in the right direction!
FWIW, I would normally be inclined to do an SQL-ish solution (essentially selecting a 'case' expression which compares the date field and selects one of three values), but sometimes with Core Data, it's easier to just pull out the sledge hammer (or rather, it feels like that's what they want us to do).
Related
I'm making an app whereas I have created a custom class which inherits from NSObject. This class contains various properties whereas one of them is a NSDate property and another one is a NSString. I would like to sort these objects in a UITableView using sections whereas each section represents the month and year of the NSDate in the object. So far, I've only managed to populate the list using a detail UITableViewCell to show the information but I'm not sure how to go about using sections as they're dynamic and not static cells.
I kind of brainstormed solutions and one of them would be to create a for-loop for all the objects and count the number of months and return that number in the numberOfSectionsInTableView: method - but I'm far from certain if this is the best and most proper way of addressing it.
Could someone help me with this?
My custom objects contain various properties but these are the ones we need to focus on:
#property (nonatomic, strong) NSString *information;
#property (nonatomic, strong) NSDate *dateAdded;
Thank you!
Erik
So give your objects a yearAndMonth property. in the getter for the property, check the instance variable. If it's !0, return it. If it ==0, use NSCalendar to calculate the year and month, turn it into a number (year*100+month), save it in the ivar, and return it.
(Also make the setter on the date property zero out the yearAndMonth property.)
Now you can write code that breaks your table view into sections based on the value of this property. For any given object, it will only be calculated once, so it shouldn't impact performance over-much.
I think a lot of it is a matter of preference. In the past for something like this I would just create a two demential array. Where the array in the array would represent the section and when you need need a section header you pull that section array out and populate the header based on the first object in that array.
NSArray *sectionArray = [self arrayAfterSorting:customObjectArray];
//section would be something like this
NSArray *section = sectionArray[section];
return section.count;
//header
NSArray *section = sectionArray[section];
CustomObject *customObject = section[0];
return customObject.whateverHeaderShouldBeBasedOnObject;
I personally try to avoid having two separate arrays and prefer to have everything built into one two dimensional array. Hope that makes sense and helps.
Edit:
If the issue is figuring out how to sort I would look at this question and answer. Although they are talking about putting it into a dictionary I am sure you can apply the same logic to an array.
Sort NSArray with NSDate object into NSDictionary by month
Hope that helps.
is your Array which contains your custom Objects sorted? Probably not i guess. I'd first start with that:
NSArray *sortedObjectArray = [youArray sortedArrayUsingComparator:^NSComparisonResult(YourObjectClass *a, YourObjectClass *b) {
return [a.dateAdded compare:b.dateAdded];}];
As you now got that, it would be the best to actually find out how many table view sections you need. To loop through your sortedArray would be the best approach there. But keep in mind to do it e.g. in viewDidLoad or some place else, where you retrieve your data, to make sure that you actually perform your search and data aggregation only once.
Doing that in numberOfSectionsInTableView would cause your app to repeat all this unnecessary calculation each time the tableView is reloaded.
So, what about the sections right? I'd recommend an NSMutableArray which represent the sections you need. This object then should contain NSMutableArrays itself, which contain your custom Objects.
But first we need to find out how many section you will actually need.Just do a for loop like this:
NSMutableArray *dateArray = [[NSMutableArray alloc] init];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDate *recentDate;
for (YourObjectClass *object in sortedObjectArray) {
if (!recentDate) {
[dateArray addObject:object.dateAdded];
recentDate = object.dateAdded;
}
else {
NSDateComponents *currentDateComponents = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:object.dateAdded];
NSDateComponents *recentDateComponents = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:recentDate];
if (recentDateComponents.year != currentDateComponents.year || recentDateComponents.month != currentDateComponents.month) {
[dateArray addObject:object.dateAdded];
recentDate = object.dateAdded;
}
}
}
So now you got your dateArray which contains all distinct dates in terms of month and year.
NSMutableArray *finalArray = [[NSMutableArray alloc] init];
for (NSDate *date in dateArray) {
NSMutableArray *subArray = [[NSMutableArray alloc] init];
for (YourObjectClass *object in sortedObjectArray) {
NSDateComponents *currentDateComponents = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:object.dateAdded];
NSDateComponents *recentDateComponents = [calendar components:NSCalendarUnitMonth | NSCalendarUnitYear fromDate:date];
if (recentDateComponents.year == currentDateComponents.year || recentDateComponents.month == currentDateComponents.month) {
[subArray addObject:object];
}
else {
break;
}
}
[finalArray addObject:subArray];
}
This should be it. Now just tell your tableView Datasource that you have finalArray.count sections. And in numberOfRowsInSection: you just get the subArray on the sectionIndex and return its count value. Hope this helps you out :)
(Didn't test the code, so be aware :))
I managed to put this code together that works, although I realised I might want to change the UI as the data wasn't presented as well as I initially thought. This is the code I made, all the other answers helped me with this!
// Create a sections NSMutableArray
_sectionsArray = [[NSMutableArray alloc] init];
// Cycle through the workdays and populate sectionsArray
NSString *currentMonth;
NSMutableArray *currentMonthArray = [[NSMutableArray alloc] init];
for (DayModel *currentModel in sortedWorkDays)
{
// Create a currentMonthArray and initialize it
// Initial currentMonth value
if (currentMonth == nil)
{
currentMonth = [self monthFromDate:currentModel.registerDate];
}
if ([currentMonth isEqualToString:[self monthFromDate:currentModel.registerDate]])
{
NSLog(#"current");
// Current month
[currentMonthArray addObject:currentModel];
if (([sortedWorkDays indexOfObject:currentModel] + 1) == [sortedWorkDays count])
{
// Complete
NSLog(#"LAST OF ALL");
[_sectionsArray addObject:currentMonthArray];
currentMonthArray = nil;
currentMonthArray = [[NSMutableArray alloc] init];
currentMonth = [self monthFromDate:currentModel.registerDate];
[currentMonthArray addObject:currentModel];
}
} else
{
// Finished with this month
NSLog(#"LAST");
[_sectionsArray addObject:currentMonthArray];
currentMonthArray = nil;
currentMonthArray = [[NSMutableArray alloc] init];
currentMonth = [self monthFromDate:currentModel.registerDate];
[currentMonthArray addObject:currentModel];
}
}
I have a simple two-tabbed table view controller. The user presses the plus button and is taken modally to another view controller to add text into textfields and select a date from the UIDatePicker.
Everything is working well, except for a duplication problem with Core Data and my dates.
In the app, the first table view displays occasions based on the entry added to the text fields but I have put in some code to check that the occasion doesn't already exist before creating it. What this means is if you have "Wedding" as an occasion and you save, if you enter another entry with "Wedding" as the occasion, rather than creating two cells in the table view with Wedding, it creates just one and when you click on it, it goes another view controller to display all entries for Wedding.
That is working well.
However when it comes to the UIDatePicker and selecting dates, duplicated items are being created.
The model is:
Transaction Entity
Occasion Entity
Date Entity
The transaction Entity has a relationship to the Occasion and Date Entity.
Let's look at some code:
The save method in the modal view controller:
- (IBAction)save:(id)sender
{
NSManagedObjectContext *context = [self managedObjectContext];
Transaction *transaction = [NSEntityDescription insertNewObjectForEntityForName:#"Transaction" inManagedObjectContext:context];
Occasion *enteredOccasion = (Occasion *)[Occasion occasionWithTitle:self.occasionTextField.text inManagedObjectContext:context];
transaction.occasion = enteredOccasion;
// Code to save the date as well - shown below
}
That is calling the occasionWithTitle method which does the NSFetchRequest check:
+ (Occasion *)occasionWithTitle:(NSString *)title inManagedObjectContext:(NSManagedObjectContext *)context
{
Occasion *occasion = nil;
// Creating a fetch request to check whether the occasion already exists
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Occasion"];
request.predicate = [NSPredicate predicateWithFormat:#"title = %#", title];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *occasions = [context executeFetchRequest:request error:&error];
if (!occasions)
{
// Handle Error
}
else if (![occasions count])
{
// If the occasions count is 0 then let's create it
occasion = [NSEntityDescription insertNewObjectForEntityForName:#"Occasion" inManagedObjectContext:context];
occasion.title = title;
}
else
{
// If the object exists, just return the last object .
occasion = [occasions lastObject];
}
return occasion;
}
The code for the date picker, also in the save method is:
Date *date = (Date *)[Date occasionWithDate:self.datePicker.date inManagedObjectContext:context];
transaction.dates = date;
Which calls:
+ (Date *)occasionWithDate:(NSDate *)enteredDate inManagedObjectContext:(NSManagedObjectContext *)context
{
Date *date = nil;
// Creating a fetch request to check whether the date already exists
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Date"];
request.predicate = [NSPredicate predicateWithFormat:#"dateOfEvent = %#", enteredDate];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateOfEvent" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *dates = [context executeFetchRequest:request error:&error];
if (!dates)
{
// Handle Error
}
else if (![dates count])
{
// If the dates count is 0 then let's create it
date = [NSEntityDescription insertNewObjectForEntityForName:#"Date" inManagedObjectContext:context];
date.dateOfEvent = enteredDate;
}
else
{
// If the object exists, just return the last object .
date = [dates lastObject];
}
return date;
}
To me, the code looks the same but of course one is passing a NSString and one is passing a selected NSDate from a UIDatePicker.
The result is when it comes to the second tabbed table view (for dates), if I create a new Transaction with 2 December 2013 as the date, and then create another entry on 2 December 2013, it creates two separate cells in the table view for the same date, which of course is not right.
Any help on this maddening issue would be very appreciated!
EDIT: On a related note, I am taking the Date selected from the DatePicker and having that displayed as the Section title of the TableView with specific formatting. I am doing that using this code:
-(NSString *)sectionDateFormatter
{
return [NSDateFormatter localizedStringFromDate:self.dates.dateOfEvent
dateStyle:NSDateFormatterLongStyle
timeStyle:NSDateFormatterNoStyle];
}
And calling this in the sectionNameKeyPath.
Ok, I am offering a different answer because you asked in responding to #Tom whether you should diddle strings to make this work. The answer is emphatically no.
I've done a LOT of date programming in Java and in Objective-C. Java's date stuff (Date/Calendar) was so lame and inadequate, first another team came along (Joda-Time) and now it's being completely redone in 8 (JSR 310). In Java, there used to be easy ways to construct things with days, etc., but apparently, all that code was done outside the purview of the calendar so when the language blew up, it quickly became apparent that it was a mess and didn't work and a huge slew of deprecations resulted.
In Cocoa, however, you have the ability to do all these things by using date components. So you can take one date, then just pull out the components from it that you are interested in, and construct another date with just those components. That's the correct way to program around having values that you are not interested in (e.g. minutes, seconds, milliseconds).
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:6];
[components setMonth:5];
[components setYear:2004];
NSInteger weekday = [components weekday];
Even if you are just going to be doing a little date programming, consider watching the WWDC 2013 session. It was surprisingly, not one of the best sessions, but it does cover some things that are a hassle.
But the date Date and Time Programming Guide is must reading, and really outstanding.
Those dates aren't really the same, they're just on the same day. UIDatePicker gives you a full NSDate, which includes the time of day. The time of day seems to be fixed when you load the picker, but if you load the same view more than once, you'll get different times. For example, when messing around with Apple's UICatalog demo, I get 2013-12-03 17:20:11 +0000 when selecting December 3. If I back out via the navigation controller and then reload the same view, I get 2013-12-03 17:20:22 +0000. Those are the same date, as the term is usually used, but they're not the same NSDate. They won't match in a predicate even though they're both on December 3.
If you're only interested in the date, not the time of day, you need to convert the NSDate from the picker into something that only has the date, with either known fixed time values or no time values. A couple of possibilities:
Use NSDateFormatter to get a string for the date, using a date format that only includes the year, month, and day.
Use NSCalendar to get the year, month, and day components for the NSDate that you get from the picker. Then either just store those values in your model, or convert those back into an NSDate with fixed, known values for the time of day (maybe make them all zero).
The second will be quicker when fetching values (since you'll be filtering on numeric values). The first may be easier to write, but filtering on string values will be slower.
If using the date components approach (recommended) do something like this with the date you get from the picker:
NSDateComponents *components = [[NSCalendar currentCalendar]
components:NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit
fromDate:date];
NSInteger year = components.year;
NSInteger month = components.month;
NSInteger day = components.day;
Then save those three values in your data model instead of an NSDate. Or combine them into a single integer value and save that:
NSInteger dateIndex = year * 10000 + month * 100 + day;
That will give you an integer value of 20131202 for December 2, 2013.
Whichever approach you use, do the same thing when fetching.
I am working through my first app and need some advice on how to approach this next task and issue. The premise of the app is the user has a table view, clicks on a plus button in the navigation bar and is presented with text fields to insert information. Upon clicking on save, that gets saved to the core data and displayed in the table view.
The table view is sectioned. Right now, I have the "date" being represented as a NSString, just to get my app off the ground, but I need to change this to a DatePicker. The sections' are based on the Dates.
I have a Core Data Model as follows:
Transaction Entity
Person Entity
Occasion Entity
Date Entity
The Transaction Entity has a relationship to each of the other entities here.
As mentioned, at first, to get my app working and off the ground, I made the Date Entity have a dateOfEvent attribute which was a NSString rather than a NSDate but of course that will not work in the long run.
I have changed my model to NSDate for this attribute and regenerated the NSManagedObject Subclasses.
Independently, I have a DatePicker working without any issues but it outputs the information to a String in a textfield.
What I want to achieve now is to use the DatePicker, select a date and have that saved to the Core Data Date Entity (dateOfEvent attribute) which I can then use in the table view as the section titles.
Here is my code for saving in the view controller:
- (IBAction)save:(id)sender
{
NSManagedObjectContext *context = [self managedObjectContext];
Transaction *transaction = [NSEntityDescription insertNewObjectForEntityForName:#"Transaction" inManagedObjectContext:context];
Date *enteredDate = (Date *)[Date occasionWithDate:self.dateTextField.text inManagedObjectContext:context];
transaction.dates = enteredDate;
// Code to save Person, Occasion, etc.
}
The enteredDate is calling a specific occasionWithDate method:
+ (Date *)occasionWithDate:(NSString *)enteredDate inManagedObjectContext:(NSManagedObjectContext *)context
Date *date = nil;
// Creating a fetch request to check whether the name of the person already exists
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Date"];
request.predicate = [NSPredicate predicateWithFormat:#"dateOfEvent = %#", enteredDate];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"dateOfEvent" ascending:YES];
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSError *error = nil;
NSArray *dates = [context executeFetchRequest:request error:&error];
if (!dates)
{
// Handle Error
}
else if (![dates count])
{
// If the person count is 0 then let's create it
date = [NSEntityDescription insertNewObjectForEntityForName:#"Date" inManagedObjectContext:context];
date.dateOfEvent = enteredDate;
}
else
{
// If the object exists, just return the last object .
date = [dates lastObject];
}
return date;
}
This does a fetchRequest to ensure I am either returning an existing date or adding a new one if that does not exist.
That is the behaviour I would like here, but of course, that method is passing a String and I need to pass a date.
With this in mind, how do I go about selecting the value of the DatePicker, adding it to the Core Data database in the same way as above (checking whether the date exists) and having this displayed in the sections of the Table View?
The reason I want to check if the date exists is because if there is an event on the 2nd December 2013, it'll be unique. However if I create another event on the 2nd December 2013, I'd want it to use the existing 2nd December, rather than create a second entry for 2nd December. The reason is my app has a tab view where the second tab is predicated by dates and so I would not want two separate 2nd December there.
This is a side note. The main thing I would like to achieve is, use the Date Picker and save the selected value to Transaction.dates.dateOfEvent to Core Data.
I know if I were to do something like date.dateOfEvent = [NSDate date]; it would be assigning the date and time now. That is not what I want here.
Any assistance would be massively appreciated.
Thanks,
EDIT: Adding in UIDatePicker Code - this first code snippet below is for saving to the textField when using NSString as the attribute
In viewDidLoad
[self.datePicker addTarget:self action:#selector(getSelection:) forControlEvents:UIControlEventValueChanged];
-(void)getSelection:(id)sender
{
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterLongStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
NSDate *date = [self.datePicker date];
NSString *formattedDateString = [dateFormatter stringFromDate:date];
self.dateTextField.text = formattedDateString;
}
Edit: The save to core data method is above - it calls occasionWithDate method and checks if the date exists already when the dateOfEvent attribute is NSString. Because I need to sort by ascending dates in the table view, I have changed the dateOfEvent to be a NSDate format
To get the current date and time, I'm putting this code in the save method:
Date *date = [NSEntityDescription insertNewObjectForEntityForName:#"Date" inManagedObjectContext:context];
date.dateOfEvent = [NSDate date];
transaction.dates = date;
That is giving me the current date and time. What I want is for the user to select a date using the UIDatePicker and whatever date is selected, for that to be saved as the dateOfEvent attribute of the Date entity which I can then use in the Sections of the Table view.
To get NSDate from your UIDatePicker object use UIDatePicket date property.
date
The date displayed by the date picker.
#property(nonatomic, retain) NSDate *date
Discussion
The default is the date when the UIDatePicker object is created. The date is ignored in the mode UIDatePickerModeCountDownTimer; for that mode, the date picker starts at 0:00. Setting this property does not animate the date picker by spinning the wheels to the new date and time; to do that you must use the setDate:animated: method.
check Apple UIDatePicker documentation
I have an Entity in core data called "Expense" with attributes "Date, Category, Amount..........". How can I list all the expenses with index list, based on "Year-Month" from Date attribute?
From apple's document and tutorials, I know how to make index list table view. But it requires to provide "Array of arrays". That's one array for all sections, and one sub-array for objects in one specific section. Now my data structure is not like this.
From my thinking, what I can do is:
fetch all the expenses first, then extract all cases of "Year-Month" with no duplication, put them into an array, which is for all the sections
then I fetch all the expenses based on every section
But I think it's kind of heavy work, can I achieve this more conveniently? Or should I change my data structure ?
Thank you guys :)
You could create a request to return only the expenses in the given range:
//Not tested
NSDateComponents* comps = [[NSDateComponents alloc] init];
NSCalendar* cal = [NSCalendar currentCalendar];
NSInteger year,month;//set these as you like
[comps setYear:year];
[comps setMonth:month];
NSDate* start = [cal dateFromComponents:comps];
month += 1;
month = (month <= 12 ? : 1);
[comps setMonth:month];
NSDate* end = [cal dateFromComponents:comps];
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"date > %# AND date < %#",start,end];
NSFetchRequest* request = [[NSFetchRequest alloc] initWithEntityName:#"Expense"];
[request setPredicate:predicate];
Then use a NSFetchedResultsController to populate your table view
This is a very general question, but the basic idea is to use an NSFetchedResultsController to display the data and an NSPredicate to do the filtering.
I'd also strongly recommend you have a look at frameworks such as Sensible TableView as it will be able to help you automatically display and filter out of the box.
I have two types of Events stored in my core data stack, each one having a timestamp. I'm interested if there's a good way to display these records in a UITableView with sections, where each section is arbitrary long (a day, week, etc).
Is there a way to convert a timestamp of a core data object into a section title, rounding down hours of the day?
So we would get:
October 5 < section title
Record 1 < records displayed in the section
Record 2
Record 3
October 6
Record 4
October 7
Record 5
...
-OR-
Week 1
Record 1
Record 2
Week 2
Record 3
...
Here's what I'm currently using to accomplish this goal, but it is limited to each section being a day.
But lets say that I have not thought about this requirement and have a list of events with just timestamps. How can I break them up into sections?
//the method used to convert a date into a number to store with the event
-(int)getDateIDFromDate:(NSDate*)date
{
int gmtOffset = [[NSTimeZone localTimeZone] secondsFromGMT];
int dateID =([date timeIntervalSinceReferenceDate]+gmtOffset)/86400;
return dateID;
}
//when inserting a record, the number is saved
newManagedObject.dayID = [NSNumber numberWithInt:[self getDateIDFromDate:date]];
//when retrieving, the number is used as a section key path
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"dayID" cacheName:#"Day"];
//the number gets converted back into the date.
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
// Display the authors' names as section headings.
// return [[[dataManager.dreamEventsController sections] objectAtIndex:section] name];
NSString* dayIndex = [[[dataManager.fetchedResultsController sections] objectAtIndex:section] name];
int dayFromReferenceDate = dayIndex.intValue;
return [dataManager.sectionDateFormatter stringFromDate:[NSDate dateWithTimeIntervalSinceReferenceDate:(dayFromReferenceDate+1)*86400]];
}
Adding (redundant) data just for displaying purposes should always be a last resort.
In slightly similar cases, I just add a category to the CoreData object, e.g.
-(NSString*)firstLetter
{
NSString *title = self.title;
if ( [title length] == 0 ) return #"?";
return [title substringToIndex:1];
}
then I just use this as the sectionNameKeyPath and all else is just the same as in a normal situation.
In your case, this category would be a little more elaborate, but the general outline could be the same. And the fact that section names will be volatile, should also be kept in mind.
A tricky (/ugly) part will be to communicate the current sectioning setting to the category. Some global or static variable could do the job efficiently.
The best way is to add a transient property to your managed object model. In that property's accessor, return a normalized NSDate with truncated hours (You can do this with NSDateComponents). Then when it's time to fetch those objects, set .. sectionNameKeyPath: to that transient property.
Updated: Let's say your NSManagedObject subclass had a transient attribute monthOfTheYear:
- (NSNumber*)monthOfTheYear
{
[self willAccessValueForKey:#"monthOfTheYear"];
NSDateComponent *dateComponent = [cachedCalendar components:NSMonthCalendarUnit fromDate:self.timestamp]; // cachedCalendar is a NSCalendar instance
[self didAccessValueForKey:#"monthOfTheYear"];
return [NSNumber numberWithInteger:dateComponent.month]; // or a normalized number that takes consideration of the year too
}
We don't create an NSString transient attribute directly because that will mess up your sorting (and you lose multi-language support).
The willAccessValueForKey: and didAccessValueForKey: are important. You should read more on their documentations.
Then when it's time to display the section titles:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSInteger monthNumber = // Get the monthOfTheYear value from the first NSManagedObject in this section.
return [[cachedDateFormatter monthSymbols] objectAtIndex:(monthNumber-1)]; // cachedDateFormatter is a NSDateFormatter instance
}
Sup bro! my advice would be to use NSDateFormatter
This is the link for Apple documentation: NSDateFormatter
I would add a category on the managedObject where you have the timeStamp,
#interface MyManagedobject (ReadableTimestamp)
-(NSString *)formatedStringFromTimestamp;
#end
#implementation MyManagedobject (ReadableTimestamp)
-(NSString *)formatedStringFromTimestamp{
//TODO: Here you apply all your fancy format, I'm just using the default
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[formatter setDateStyle:dateStyle];
[formatter setTimeStyle:timeStyle];
NSString *result = [formatter stringForObjectValue:self.timestamp];
return result;
}
#end
I hope this helps, good times.
If you're some kind of an optimization freak (like myself) you can define your timeFormatter as an static so that you don't need to build it everytime.