I have an NSArray that is extracted from core data. The entities in the array have a date attribute (of type NSDate ofcourse). What I want to do is this:
The array needs to be displayed in a table view,
the sections, and their titles are months , of the objects in the array
For example if I have 3 objects (april 1, april 3 & july 7) there should be 2 sections:
- april 2012(2 obj)
- july 2012 (1 obj)).
How do I split the array like this?
Try using a NSFetchedResultsController and a custom sectionNameKeyPath which should be a method in your NSManagedObject subclass.
The fetchedResultsController can be set up as following:
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"MyObject"];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES];
request.sortDescriptors = #[sortDescriptor];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:managedObjectContext sectionNameKeyPath:#"monthAsString" cacheName:nil];
You need to implement the monthAsString method in your managedObject subclass. Allocate the NSDateFormatter only once, because it won't perform well if you allocate a new instance for every call.
- (NSString *)monthAsString {
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"MMMM yyyy";
});
NSString *dateString = [formatter stringFromDate:self.date];
return dateString;
}
Related
I am storing a string input from textfield and its created dateandtime. I am storing date values in coredata with key messageDate which is a string data type. I am converting my date values to string and storing it in coredata.I am able to see the array of stored date and text values like below
timeAndDateMsgArr (
"2016-11-02 17:59 PM",
"2016-11-02 18:08 PM",
"2016-11-02 18:08 PM"
)
textArr (
"Hai",
"The",
"I'm"
)
I am able to see the stored text and its created date in UITableView by adding two labels in CustomCell. But i need to group the text by date. I have already tried many solutions and below is one of them
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"msgHistory" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:#"messageDate" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil
cacheName:#"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
NSLog(#"fetchcontroller %#",_fetchedResultsController);
return _fetchedResultsController;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
NSString *rawDateStr = [[[self.fetchedResultsController sections] objectAtIndex:section] name];
NSLog(#"raw %#",rawDateStr); //****** i am getting null value here************//
// Convert rawDateStr string to NSDate...
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[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;
}
Posting a pic to get some idea,current output is like
My expected output will be something like below, where i will show date in place of month
But i need to show messages group by date. I have tried apple sample code which suits my requirement where the date key is stored in date data type but in my case i am storing date in string data type. In my case i cannot change the data type because it effects the code. Is their any alternative solution or i need to change the data type? Any help will be really appreciated.
Apple sample code reference link
REF LINK
You can cleanly solve this by adding a non-optional, non-transient, String -messageDateSectionId attribute to your Core Data entity.
Have this -sectionId attribute be derived from the main -messageDate attribute; whenever you set -messageDate, also set -messageDateSectionId to your custom format.
Thus, you will be saving this derived -sectionId attribute along with your main attribute.
Next, when you create the -fetchController, set -sectionNameKeyPath to your -sectionId attribute. Everything in January 1-31, 2013 will have the same -sectionId, and so will be grouped together in the table.
For the -titleForHeaderInSection, you can simply return the -fetchController.sections[SECTION_NUMBER].name value to show your -sectionId string.
I want to perform a predicate based on a stored attribute's (NSDate) month.
I understand that comparing a start and end date (manually set) allows this.
But I feel like a custom method in the Managed Object's class should allow this fairly easily as well? Well, I tried, but I can't seem to access a simple method by having the predicate fire it. Is this in any way possible?
Managed Object files:
.h file:
#property (nonatomic,retain) NSNumber *monthOfDate;
-(NSNumber *)monthOfDate;
.m file:
#synthesize monthOfDate;
-(NSNumber *) monthOfDate
{
NSDate *date = [self start];
NSDateComponents *components = [[NSCalendar currentCalendar]components: NSCalendarUnitMonth fromDate:date];
NSNumber *month = [NSNumber numberWithInteger:[components month]];
return month;
}
This is my predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"client == %# AND monthOfDate == %d", clientMO,int 6];
It returns an error: keypath monthOfDate not found in entity. I presume because it's not an actual attribute? How can I make it detectable?
Update
The .h and .m files above are from the auto-generated Event class (NSmanaged object). I might move my custom code to a category later but so far no db model revisions are necessary.
Below is the FetchedResultsController (in a uiviewcontroller class) setup with mentioned predicate:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:#"Event" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sortfix = [[NSSortDescriptor alloc]initWithKey:#"timeStamp" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortfix,nil]];
[fetchRequest setFetchBatchSize:20];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"client == %# AND monthOfDate == %d", clientMO,int 6];
[fetchRequest setPredicate:predicate];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:sectionKeyPathProperty
cacheName:nil];
//sectionKeyPathProperty is a variable that let's me choose one of multiple transient properties I have created for creating relevant sections.
self.fetchedResultsControllerClient = theFetchedResultsController;
_fetchedResultsControllerClient.delegate = self;
return _fetchedResultsControllerClient;
This turns out to only be possible when the MO's have already been loaded into memory. (fetched)
In my case with a FRC this is not the case thus I will have to create a separate month attribute that is saved on MO creation.
Thanks to user Run Loop for responding on a 4 year old question!
I have three sections for an Events entity:
Upcoming
Today
Past
Here is FRC setup:
- (NSFetchedResultsController *)fetchedResultsController
{
if(_fetchedResultsController!=nil)
{
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *firstSort = [[NSSortDescriptor alloc] initWithKey:#"modified"
ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc]initWithObjects:firstSort, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
self.fetchedResultsController = [[NSFetchedResultsController alloc]initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:#"sectionIdentifier"
cacheName:nil];
self.fetchedResultsController.delegate = self;
return self.fetchedResultsController;
}
Using a Transient property for the Entity, I setup the the sections as:
- (NSString *)sectionIdentifier
{
[self willAccessValueForKey:#"sectionIdentifier"];
NSString *tmp = [self primitiveSectionIdentifier];
[self didAccessValueForKey:#"sectionIdentifier"];
if (!tmp)
{
NSDate *dateToCompare = [self getUTCFormateDate:[self modified]];
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDate* now = [NSDate date];
NSDateFormatter *format = [[NSDateFormatter alloc] init];
format.dateFormat = #"dd-MM-yyyy";
NSString *stringDate = [format stringFromDate:now];
NSDate *todaysDate = [format dateFromString:stringDate];
NSUInteger differenceInDays =
[calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:dateToCompare] -
[calendar ordinalityOfUnit:NSDayCalendarUnit inUnit:NSEraCalendarUnit forDate:todaysDate];
NSString *sectionString;
switch (differenceInDays) {
case -1:
sectionString = #"Past";
break;
case 0:
sectionString = #"Today";
break;
case 1:
sectionString = #"Upcoming";
break;
}
tmp = sectionString;
[self setPrimitiveSectionIdentifier:tmp];
}
return tmp;
}
Currently the sections appear in descending order, where Upcoming is first, then Today, and then Past. I'd like to show Today's section first.
How can I display the sections in the following order?
Section 0: Today
Section 1: Upcoming
Section 2: Past
The only thing controlling the order is the sort descriptor you have:
NSSortDescriptor *firstSort = [[NSSortDescriptor alloc] initWithKey:#"modified"
ascending:NO];
So you need to change that. You can make it ascending, but you describe a sort order which isn't directly linked to the date.
Because what you describe is dependent upon the current date you will need to make some modifications so that you have order information stored persistently into the model and which you update each day. You can then add this as your first your sort descriptor to use that, and keep the existing sort descriptor for ordering inside each section.
The information you need to store is as per the comment from #Volker, with a simple index number for each section.
I am trying to reload my tableview with the parameters received from changing a UIDatepicker. Currently I have the following method that captures the data and sends it to the UIButton (which works). I am trying to reset the container view that houses the UITableViewController which is called DisplayTableViewController but it is not reloading the NSFetchController. Any suggestions about how to debug or fix this issue?
-(void)changeDate:(UIDatePicker *)sender {
//NSDate Formatter
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"ddMMyyyy"];
//Convert to string
NSString *stringFromDate = [dateFormatter stringFromDate:sender.date];
//Send to UIButton
[dateLabel setTitle:stringFromDate forState:UIControlStateNormal];
DisplayTableViewController *tbc = (DisplayTableViewController *)self.childViewControllers[0];
[tbc.tableView reloadData];
}
EDIT
if ([[segue identifier] isEqualToString:#"ShowDisplayResults"])
{
//Format Date for Seague
NSDateFormatter *todaysdate = [[NSDateFormatter alloc] init];
[todaysdate setDateFormat:#"ddMMyyyy"];
//Convert to string for Seague
NSString *stringFromDate = [todaysdate stringFromDate:[NSDate date]];
// Store the text we entered to dateSeague
dateSeague =stringFromDate;
// Get reference to the destination view controller
DisplayTableViewController *targetVC = [segue destinationViewController];
// Pass any objects to the view controller here, like...
targetVC.dateSeague = dateSeague;
targetVC.Ex = self.Ex;
}
NSFETCHCONTROLLER IN CONTAINER
- (NSFetchedResultsController *)fetchedResultsController {
NSLog(#"Test Date for second seague = %#", dateSeague);
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
//Query Entity
NSEntityDescription *entity = [NSEntityDescription entityForName:#"WorkoutDetails"inManagedObjectContext:managedObjectContext];
//Fetch Entity
[fetchRequest setEntity:entity];
//Sort Returned Values
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"weight" ascending:NO];
//Fetch Sorted Array
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
//Set Return Size
[fetchRequest setFetchBatchSize:20];
//Predicate Results Based on ID
NSPredicate *sortName = [NSPredicate predicateWithFormat:#"exid == %#", Ex.name];
//Get NSDate format from String
NSString *stringFromDate = dateSeague;
NSDateFormatter *df = [[NSDateFormatter alloc] init];
//Convert Back to NSDate
[df setDateFormat:#"ddMMyyyy"];
NSDate *inputedDate = [df dateFromString: stringFromDate];
//Set Predicate
NSPredicate *sortDate = [NSPredicate predicateWithFormat:#"date == %#", inputedDate];
//Compound Predicate Array
NSPredicate *placesPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:sortName, sortDate, nil]];
//Fetch Predicate Compound Array
[fetchRequest setPredicate:placesPredicate];
//Continue Fetching!
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:nil
cacheName: nil];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
First of all self.childViewControllers[0] will get you the first view controller on the view controller hierarchy and you want this only if you have one view controller that contains another view controller.
Second it's not a good practice to use this approach since you don't really know the state of the view controller when you call this and it's possible that the view controller at that index won't be the same class as you expected.
So I suggest that you implement a delegate for your view controller or view that holds the date picker and when the date is changed and you do all the things that you want to do with the date, call the delegate. If you don't want to do anything with the date, you just want to pass it to the view controller, then you can set the date picker delegate to be your DisplayTableViewController
As a side note, I don't know the hierarchy that you have right now and if you don't find my answer helpful please add more details to your question (the hierarchy and the flow that you want to get)
Are query params for the NSFetchRequest of the NSFetchController changing? If so, make sure that you use deleteCacheWithName if it is using a cache, and then reconfigure the NSFetchController.
I know that fetched results controller have the section name key path can divide fetched results into sections.
But how could I divide NSDate into sections for each day or each month?
Or any other ways to solve this problem?
Thanks.
What you need to do is to create a transient property on your data object, and then sort your fetched results accordingly. For a TVGuide I've worked on, I needed to sort results by airDay, and ended up sorting the events by startDate, and using the transient property for section key name path:
In Event.m:
-(NSString*) airDay
{
NSDateFormatter *dayFormatter=[[NSDateFormatter alloc] init];
[dayFormatter setLocale:[NSLocale currentLocale]];
[dayFormatter setDateStyle: NSDateFormatterMediumStyle];
[dayFormatter setDoesRelativeDateFormatting: YES];
return [dayFormatter stringFromDate:self.startDate];
}
The matching fetchrequest
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Event" inManagedObjectContext:[Database db].managedObjectContext];
[fetchRequest setEntity:entity];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:
[NSSortDescriptor sortDescriptorWithKey:#"startDate"
ascending:YES],
nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *frc = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:[Database db].managedObjectContext
sectionNameKeyPath:#"airDay"
cacheName:#"SearchEvents"];
Please refer to the Apple sample code of "DateSectionTitles", you can search this in the Xcode help.
it helps a lot!!