I wrote this code to reload the UItableView with an events that has the same date as the current date when the user click on todays events UIButton in the main view controller but the problem is the below code is not reloading the right data (it just gives the initial data without comparing the date of the event with the date of the calendar in the IPhone), my data comes from a json file within the project and consists from NSArray of events, each has a different value for each key and one of these keys is the data of that event ("date"), can anyone plz clarify for me why the below code is not returning the right data ??
#implementation MainViewController {
NSArray *_events;
}
....
- (IBAction)upcomingEvents:(id)sender {
NSDate *currDate = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"dd-MM-YYYY"];
NSString *dateString = [dateFormatter stringFromDate:currDate];
for (Events *event in _events){
if([event.date isEqualToString:dateString]){
[self.myTableView reloadData];
}
}
}
If you're using a UITableViewDataSource you should make sure that it returns only the events that match your condition [event.date isEqualToString:dateString]
You can do
NSArray * dateEvents = [_events filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Events * event, NSDictionary *bindings)
{
return [event.date isEqualToString:dateString];
}];
Then you can use dateEvents for your UITableViewDataSource.
Related
I have an NSMutableArray called self.objectArray, that contains custom objects. Each object holds an NSDictionary and two other string objects. Actually I need to work only with the dictionary. Every dictionary contains a key named keyDate which holds an NSString that look like this: MM/dd/yy HH:mm:ss.
I would like to sort the array based on their keyDate. The object with the oldest date should be the first object and so on. I've found some questions, that looked helpful and I could create the code that you can see below, but I get an error everytime I run it. As I think NSSortDescriptor won't be the right tool since my keys aren't key value compliant.
PNMessage 0x125c0590> valueForUndefinedKey:]: this class is not key value coding-compliant for the key keyDate.'
NSSortDescriptor *dateDescriptor = [NSSortDescriptor
sortDescriptorWithKey:#"keyDate"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:dateDescriptor];
NSArray *sortedEventArray = [self.objectArray
sortedArrayUsingDescriptors:sortDescriptors];
self.finallySorted = [sortedEventArray mutableCopy];
If it's possible I would do it with sort descriptor, however I think there should be some other options, but can't figure out its proper implementation.
So I can also catch every object's keyDate with a for loop, but don't know how can I sort them based on the value. I would really appreciate if somebody could show me the right way.
for(PNMessage *mg in self.objectArray)
{
NSLog(#" test log %#", mg.message[#"keyDate"]);
}
I already checked this answer:
How to sort an NSMutableArray with custom objects in it?
but the structure of my object is different.
My first code based on this question, but it doesn't worked.
How to sort an NSMutableArray with custom objects in it?
UPDATE: my try based on Kaan's answer (doesn't works yet)
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MM/dd/yy HH:mm:ss"];
}
NSArray *sortedArray = [self.object sortedArrayUsingComparator:^NSComparisonResult(PNMessage *obj1, PNMessage *obj2) {
NSString *date1String = obj1.message[#"keyDate"];
NSString *date2String = obj1.message[#"keyDate"];
NSDate *date1 = [formatter dateFromString:date1String];
NSDate *date2 = [formatter dateFromString:date2String];
if ( date1 < date2 ) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( date1 > date2 ) {
return (NSComparisonResult)NSOrderedDescending;
}
return (NSComparisonResult)NSOrderedSame;
}];
I would consider using the sortedArrayUsingComparator method
Assuming your custom class is called PNMessage:
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setFormat:#"MM/dd/yy HH:mm:ss"];
}
NSArray *sortedArray = [self.objectArray sortedArrayUsingComparator:^NSComparisonResult(PNMessage *obj1, PNMessage *obj2) {
NSString *date1String = obj1[#"keyDate"];
NSString *date2String = obj1[#"keyDate"];
NSDate *date1 = [formatter dateFromString:date1String];
NSDate *date2 = [formatter dateFromString:date2String];
return [date1 compare:date2];
}];
Tip: If you decide on following this, make sure you declare your NSDateFormatter instance as static outside of the sorting body, since allocating Formatters in iOS can be very expensive and cause serious performance penalties.
I am looking to convert a NSString to an NSDate and would just like to ask a few questions about this.
The purpose of the conversion is to take a section header in a table view which is a string derived from an NSDate, and to pass it back to a UIDatePicker, so that the date picker can use that date in the string.
I've been reading some posts about this and there are tons of formats to work through.
My string date format is in the form:
March 10, 2014 for American formats and 10 March 2014 for UK formats.
What would I need to do to:
1) Get that date to translate over to the UIDatePicker appropriately
2) Work with different country formats so that the date picker is updated for UK and US users.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"d-MMMM-YYYY"];
NSDate *dateFromString = [[NSDate alloc] init];
dateFromString = [dateFormatter dateFromString:sectionTitle];
dateFromString is currently null.
I've been looking at https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html , Converting NSString to NSDate (and back again) and http://waracle.net/iphone-nsdateformatter-date-formatting-table/ but am not sure how to proceed.
Any assistance would be appreciated on this
I know this isn't directly answering the question, but it makes several of the above comments make more sense:
How to create the data source structure for a table view:
NSMutableArray* sections = [NSMutableArray array];
for (<each section>) {
NSMutableDictionary* section = [NSMutableDictionary dictionary];
[section setObject:sectionTitle forKey:#"title"];
[section setObject:sectionNSDate forKey:#"date"];
[section setObject:otherStuff forKey:#"other_stuff"];
NSMutableArray* rows = [NSMutableArray array];
for (<each row>) {
NSMutableDictionary* row = [NSMutableDictionary dictionary];
[row setObject:rowTitle forKey:#"title"];
[row setObject:image forKey:#"image"];
[rows addObject:row];
}
[section addObject:rows forKey:#"rows"];
[sections addObject:section];
}
It's easy to keep track of & update section and row data, the number of sections and number of rows methods virtually write themselves, you don't need to re-derive data if something is scrolled away and comes back (since you can cache it in the dictionary).
Im quiet new to Xcode and objective-C and Im trying to build this app about events, I have two buttons in my mainViewController one to show today's events and the other will show any future events, my data are coming from a json file [which will include several keys one of them is the date of the event] and the way I thought about how the app will show this by matching the date of the event with the date of the calendar within the app and and I used this code:
#implementation MV_HomeViewController {
NSArray *_events;
}
.....
.......
- (IBAction)upcomingEvents:(id)sender {
// Events *event = [_events object:.row];
//Events *event = [_events objectForKey:#"Events"];
NSDate *currDate = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"dd-MM-YYYY"];
NSString *dateString = [dateFormatter stringFromDate:currDate];
if([_events objectForKey:#"date"] != dateString){
[[self myTableView] reloadData];
}
}
but it is giving me an error # [_events objectForKey:#"date"] which is totally make sense because I have to call my json data again within this action and that is I don't know how to do ?? can anyone plz tell me how to do that or how to make the above code works for my app?
I would be very appreciated.
PS, for more details about how my json file looks like plz see the previous question that I have posted.
Thanks,
I tried the implement the answer below but this with this code:
for (NSDictionary *event in _events) {
// Now you can use [event objectForKey:#"date"] to retrieve the event date
if ([[event objectForKey:#"date"] isEqualToString:dateString]){
[[self myTableView] reloadData];
}
}
but this time Im getting an error saying --> -[Events objectForKey:]: unrecognized selector sent to instance 0x74303f0
Could anyone plzzz tell me what is the problem now!! Im sooo confused ...
objectForKey: is a NSDictionary method, not a NSArray method. To retrieve an event dictionary from the array, you would use objectAtIndex:. If you want to loop over all the event dictionaries in the array, you can use fast enumeration like so:
for (NSDictionary *event in _events) {
// Now you can use [event objectForKey:#"date"] to retrieve the event date
}
When comparing strings, use isEqualToString: rather than the equality operator (==) or inequality operator (!=).
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 am listing the events in my app. User can create, edit and delete the events. In viewDidLoad method I fetch all events I need and push them into an array. It works like expected.
For creating, editing and deleting events I use EKEventEditViewController and EKEventViewController which works pretty well. In delegate methods of the controllers I make the changes I need on my array and reload my view.
Of course I would like also know and handle, if user make some changes from another app (like built-in calendar app). So I observe EKEventStoreChangedNotification. From that notification I get only "changes have been occurred" and not which event or from which app. Actually what I want to know is, if the change has been occurred from my app or another app and which events have been changed. Since I already handle the changes(from my app) in EKEventEditViewControllerDelegate method, I do not need to handle them again.
If I do not know which objects have been changed, I have to fetch ans sort all of them.
For now I have only 5 events in the calendar(development device), of course it is not a problem to fetch and sort all events, but if user has more then 1000, it is overkill for maybe only one event change.
So my question is: How to handle EKEventStoreChangedNotification?
You can detect exactly which event has been changed by the following code [Disclaimer code is not my idea, I have found it in another Stack Overflow answer and modified it a little bit].
I'm using a lib called "JSCalendarManager" for interaction with eventstore and in my case as the events created using my App and synced with iCalendar I already saved their eventIdentifier in local DB , I can retrieve my time bound to search for events in iCalendar and get match for changed one.
+(void)iCloudStoreChanged:(NSNotification*)eventStoreChangeNotification{
NSArray* allScheduleRecords =[self getAllScheduleRecordSyncedToICalendar];
NSDate* startDate = [NSDate new];
NSDate* endDate = [NSDate new];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
if (allScheduleRecords.count >= 2) {
startDate = [dateFormatter dateFromString:[[allScheduleRecords firstObject] objectForKey:#"meetingTime"]];
endDate = [dateFormatter dateFromString:[[allScheduleRecords lastObject] objectForKey:#"meetingTime"]];
}else if (allScheduleRecords.count > 0){
startDate = [dateFormatter dateFromString:[[allScheduleRecords firstObject] objectForKey:#"meetingTime"]];
NSDate *today = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:(NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit) fromDate:today];
components.day = 1;
endDate = [gregorian dateFromComponents:components];
}else{
}
NSArray *ekEventStoreChangedObjectIDArray = [eventStoreChangeNotification.userInfo objectForKey:#"EKEventStoreChangedObjectIDsUserInfoKey"];
[calendarManager findEventsBetween:startDate
and:endDate
withSearchHandler:^(BOOL found, NSError *error, NSArray *eventsArray) {
[eventsArray enumerateObjectsUsingBlock:^(EKEvent *ekEvent, NSUInteger idx, BOOL *stop) {
// Check this event against each ekObjectID in notification
[ekEventStoreChangedObjectIDArray enumerateObjectsUsingBlock:^(NSString *ekEventStoreChangedObjectID, NSUInteger idx, BOOL *stop) {
NSObject *ekObjectID = [(NSManagedObject *)ekEvent objectID];
if ([ekEventStoreChangedObjectID isEqual:ekObjectID]) {
// Log the event we found and stop (each event should only exist once in store)
NSLog(#"calendarChanged(): Event Changed: title:%#", ekEvent.title);
[self updateAppointmentForEvent:ekEvent];
*stop = YES;
}
}];
}];
}];}
Instead of fetching all events, can you not update only the events that are onscreen/active.