How to show Single date in multiple events reload in MBCalendarKit - ios

I am using a Calendar view in my project. I am using a MBCalendarKit. That time single date in single event show. But I want single date on multiple events show. But how it possible please help.
- (void) viewWillAppear: (BOOL)animated{
NSArray *title = [_caldevice valueForKey:#"pill"];
// NSLog(#"event name fetch %#",title);
NSArray *date =[_caldevice valueForKey:#"datetaken"];
// NSLog(#"event fetch %#",date);
NSArray*dose= [_caldevice valueForKey:#"dose"];
NSString *title1;
NSString*title2;
NSDate *date1;
NSData *imgdata;
CKCalendarEvent *releaseUpdatedCalendarKit;
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"dd-MM-yyyy";
for (int i = 0; i < [date count]; i++){
title1 = NSLocalizedString(title[i], #"");
title2 = NSLocalizedString(dose[i], #"");
NSString *combined = [NSString stringWithFormat:#"%# - %#", title1, title2];
date1 = [dateFormatter dateFromString:date[i]];
releaseUpdatedCalendarKit = [CKCalendarEvent eventWithTitle:combined andDate:date1 andInfo:Nil];
// NSLog(#"Event: %# , %#",combined,date1);
// releaseUpdatedCalendarKit = [CKCalendarEvent eventWithTitle:combined andDate:date1 andInfo:Nil andColor:[UIColor blueColor]];
self.data[date1] = #[releaseUpdatedCalendarKit];
}
}

You're looping over a bunch of events and for each event, you're *replacing the previously assigned array with a new one, containing a single element.
Replace this:
self.data[date1] = #[releaseUpdatedCalendarKit];
with something more like this:
// 1. First, get the previous events for that day.
NSMutableArray <CKCalendarEvent *> *events = self.data[date1].mutableCopy;
// 2. If events exist, append the event, otherwise create an empty array with the new event.
if (events) {
[events addObject: newEvent];
}
else {
events = #[newEvent];
}
// 3. Set the events for the date key.
self.data[date1] = events;
This way you're doing an "add or create" operation, instead of overwriting each time.
Disclosure: I wrote and maintain MBCalendarKit.

Related

Find nearest date in string array

So, I've got an sorted NSArray that contains NSString object (downloaded from a server), with the format: yyyy-MM-dd.
It's pretty much like this:
NSArray <NSString *> *dates = #[#"2017-06-25",
#"2017-06-26",
#"2017-06-27",
#"2017-06-28",
#"2017-06-30",
#"2017-07-01",
#"2017-07-02",
#"2017-07-03"];
So, today is 2017-06-29, and it's not in the array. How do I get the next nearest one? In this sample is 06-30, but it might be 07-01 if 06-30 doesn't exist...
Update
So people are asking me about what I've attempted to do. So it's like this (not very effective, but work)
Find if today is in the array (if yes, return)
Loop dates:
2.1 Convert dateString to date
2.2 Compare if date is greater than today => return if YES
If not found in step#2, return last object in dates array.
Actual code:
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.dateFormat = #"yyyy-MM-dd";
NSDate *today = [NSDate date];
NSUInteger index = [dates indexOfObject:[formatter stringFromDate:today]];
// Step 1
if (index == NSNotFound) {
// Step 2: Loop converted
NSInteger i = 0;
for (NSString *date in dates) {
// Step2.1: find the next nearest date's index
NSDate *convertedDate = [formmater dateFromString:date];
// Step2.2: Compare
if ([convertedDate intervalSinceDate:today] > 0) {
index = i;
break;
}
i++;
}
// Step 3: Still not found, index = last index
if (index == NSNotFound) index = i-1;
}
return dates[index];
This doesn't look so good because I might reload the dates array pretty much. Can I have a better solution?
Your algorithm is not bad, though your code doesn't appear to implement it (no sort?). If you'd like to improve it consider this:
First there is probably little point in doing a first scan to check for an exact match - that is potentially a linear search (implemented by indexOfObject:) through an unordered array, and if it fails you have to scan again for a close match, just do them at the same time.
Second there is no advantage in sorting, which is at best O(NlogN), as a linear search, O(N), will find you the answer you need.
Here is a sketch:
Convert the date you are searching for from NSString to NSDate, call it, say, target
Set bestMatch, an NSString to nil. Set bestDelta, an NSTimeInterval, to the maximum possible value DBL_MAX.
Iterate over your dates array:
3.1. Convert the string date to an NSDate, say date
3.2. Set delta to the difference between date and target
3.3. If delta is zero you have an exact match, return it
3.4. If delta is better than bestDelta, update bestDelta and bestMatch
After iteration bestMatch is the best match or nil if there wasn't one.
That is a single iteration, O(N), early return on exact match.
HTH
Please find the simplest solution for your problem. Updated solution based on sorting order!
We can use NSPredicate Block to solve.
static NSDateFormatter* formatter = nil;
static NSDate* today = nil;
// return an NSDate for a string given in yyyy-MM-dd
- (NSDate *)dateFromString:(NSString *)string {
if (formatter == nil) {
formatter = [NSDateFormatter new];
formatter.dateFormat = #"yyyy-MM-dd";
}
return [formatter dateFromString:string];
}
// Helps to return today date.
-(NSDate*) getTodayDate {
if (today == nil) {
today = [NSDate date];
}
return today;
}
// Helps to find nearest date from Array using Predicate
-(NSString*)findNearestDate:(NSArray*)dateArray {
today = nil;
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSString *dateString, NSDictionary *bind){
// this is the important part, lets get things in NSDate form so we can use them.
NSDate *dob = [self dateFromString:dateString];
NSComparisonResult result = [[self getTodayDate] compare:dob];
if (result == NSOrderedSame || result == NSOrderedAscending) {
return true;
}
return false;
}];
// Apply the predicate block.
NSArray *futureDates = [dateArray filteredArrayUsingPredicate:predicate];
if ([futureDates count] > 0) {
// Sort the Array.
futureDates = [futureDates sortedArrayUsingSelector: #selector(compare:)];
return [futureDates objectAtIndex:0];
}
return nil;
}
NSArray <NSString *> *dates = #[#"2017-06-25",
#"2017-06-26",
#"2017-06-27",
#"2017-06-28",
#"2017-06-30",
#"2017-07-01",
#"2017-07-02",
#"2017-07-03"];
NSLog(#"Nearest Date: %#", [self findNearestDate:dates]);
Answer: Nearest Date: 2017-06-30
1. Input
So you have an array of NSString like this
// input
NSArray<NSString *> * words = #[#"2017-06-25",
#"2017-06-26",
#"2017-06-27",
#"2017-06-28",
#"2017-06-30",
#"2017-07-01",
#"2017-07-02",
#"2017-07-03"];
2. Converting the array of NSString into an array of NSDate
First of all you need to convert the each input string into an NSDate
NSMutableArray<NSDate *> * dates = [NSMutableArray new];
NSDateFormatter * dateFormatter = [NSDateFormatter new];
dateFormatter.dateFormat = #"yyyy-MM-dd";
for (NSString * word in words) {
[dates addObject:[dateFormatter dateFromString:word]];
}
3. Finding the nearestDate
Now you can find the nearest date
NSDate * nearestDate = nil;
NSTimeInterval deltaForNearesttDate = 0;
NSDate * now = [NSDate new];
for (NSDate * date in dates) {
NSTimeInterval delta = fabs([date timeIntervalSinceDate:now]);
if (nearestDate == nil || (delta < deltaForNearesttDate)) {
deltaForNearesttDate = delta;
nearestDate = date;
}
}
4. Conclusion
The result is into the nearestDate variable so
NSLog(#"%#", nearestDate);
Wed Jun 28 00:00:00 2017

Create Events for MBCalendarKit in iOS

I imported MBCalendar Kit into my project, and I don't know how to add an event or array of events in calendar. I found this code:
NSMutableDictionary *eventsDict = [[NSMutableDictionary alloc] init];
for (int i =0; i< eventsArray.count ;i++)
{
// Create events
eventsDict = eventsArray[i];
CKCalendarEvent* aCKCalendarEvent = [[CKCalendarEvent alloc] init];
aCKCalendarEvent.title = [eventsDict objectForKey:#"email"];
aCKCalendarEvent.date = date; //[eventsArray objectForKey:#"phone"];
aCKCalendarEvent.address = [eventsDict objectForKey:#"addrLine1"];
aCKCalendarEvent.image = [eventsDict objectForKey:#"pPic"];
aCKCalendarEvent.name = [eventsDict objectForKey:#"fname"];
aCKCalendarEvent.appDate = [eventsDict objectForKey:#"apntDt"];
aCKCalendarEvent.notes = [eventsDict objectForKey:#"notes"];
aCKCalendarEvent.phone = [eventsDict objectForKey:#"phone"];
[myeventsArray addObject: aCKCalendarEvent];
}
[_data setObject:myeventsArray forKey:date];
but I don't know where to write it, or how to use it. Can anyone help me?
Thank you.
I'm working with this Framework and I've had the same issues.
What worked for me was to use the NSDate+Components category, specifically the dayWithDay:month:year method to create the dates for the events, then create as many events as you want the way you're doing it, encapsulate all the events that are on the same day in an array and lastly setting that array as an object for the NSDictionary data with the previously created as the key to that array. Here's an example:
NSDate *eventDate1 = [NSDate dateWithDay:8 month:8 year:2014];
NSDate *eventDate2 = [NSDate dateWithDay:9 month:8 year:2014];
CKCalendarEvent *event1 = [CKCalendarEvent eventWithTitle:#"Event 1" andDate:eventDate1 andInfo:nil];
CKCalendarEvent *event2 = [CKCalendarEvent eventWithTitle:#"Event 2" andDate:eventDate2 andInfo:nil];
NSArray *today = [NSArray arrayWithObjects:event1, nil];
NSArray *tomorrow = [NSArray arrayWithObjects:event2, nil];
[[self data] setObject:today forKey:eventDate1];
[[self data] setObject:tomorrow forKey:eventDate2];
Hope this helps :D
I'm working on my own framework based on this but with an iOS7 native feel, it's not finished yet but here is the repo:
https://github.com/AndoniV/CalendarBar_iOS7_Style.git

Global NSDate variable seems to make app crash

I have a global variable in FirstViewController.h
extern NSString *dateString;
it records the date/time of when I press saveText button. There is another button called readText which pushes a UITableView. The first cell on this table will print the date saved.
However, this only works if I first press the saveText button. Otherwise, if I just press readText, it crashes. How can I get it so that if there is no current datetime saved, that it doesn't crash?
Here is where I wrote the timestamp for saveButton:
-(IBAction)saveText:(id)sender{
//code to save text, irrelevant to the question//
dateString = [NSDateFormatter localizedStringFromDate:[NSDate date]
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];
NSLog(#"%#",dateString);
}
and here is the code to load the tableviewcells with the timestamp
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *str = #"Text- saved on: ";
str = [str stringByAppendingString: dateString];
self.list = [[NSArray alloc] initWithObjects: str, nil];
}
In my methods that use the variable dateString,
I used:
if([dateString length] == 0){
//perform standard procedure
}
else {
//what I want it to do
}

Chronologically organize Cells in TableView

I have two UITableViewCell's that each correspond to a Twitter Tweet or a Instagram Pic. As of now I have set it so that It will alternate between the two cells and have It go from an Twitter Tweet to an Instagram Pic. However I want to organize the UITableView so that it organizes both the Twitter Tweets and Instagram Pics chronologically in the TableView. Both the date keys for the Instagram API and the Twitter API are different. The Twitter API has a time key of created_at and Instagram's key is created_time. How would I organize both these Arrays? P.S. Both the arrays which the Tweets and Pics are in are NSMutableArray's.
Here is the Code that I have tried thus far:
// Setup for Cells
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *twitterCell = [self createTwitterCell:indexPath];
UITableViewCell *instagramCell = [self createInstagramCell:indexPath];
if (indexPath.row % 2 == 0) {
return twitterCell;
}else{
return instagramCell;
}
}
// Array Updater
- (void)updateArrays {
instaPics = self.timelineResponse[#"data"];
totalFeed = [tweets arrayByAddingObjectsFromArray:instaPics];
[self sortArrayBasedOndate:totalFeed];
}
// Current flawed method to organize by date
- (void)sortArrayBasedOndate {
NSDateFormatter *fmtDate = [[NSDateFormatter alloc] init];
[fmtDate setDateFormat:#"yyyy-MM-dd"];
NSDateFormatter *fmtTime = [[NSDateFormatter alloc] init];
[fmtTime setDateFormat:#"HH:mm"];
NSComparator compareDates = ^(id string1, id string2)
{
// Instagram Date Retrieval
NSDictionary *instagram = self.instaPics;
NSString *createdAt = instagram[#"created_time"];
int createdAtN = [createdAt intValue];
NSTimeInterval timestamp = (NSTimeInterval)createdAtN;
NSDate *date1 = [NSDate dateWithTimeIntervalSince1970:timestamp];
// Twitter Date Retrieval
NSDictionary *twitter = self.tweets;
NSDate *date2 = twitter[#"created_at"];
return [date1 compare:date2];
};
NSSortDescriptor * sortDesc1 = [[NSSortDescriptor alloc] initWithKey:#"start_date" ascending:YES comparator:compareDates];
[totalFeed sortUsingDescriptors:#[sortDesc1]];
}
If you guys need any more code excerpts feel free to ask :)
It seems like you're on the right track. Why don't you also format Twitter's time to match your formatting for Instagram's time?
NSComparator compareDates = ^(id string1, id string2)
{
// Instagram Date Retrieval
NSDictionary *instagram = self.instaPics;
NSString *createdAt = instagram[#"created_time"];
int createdAtN = [createdAt intValue];
NSTimeInterval timestamp = (NSTimeInterval)createdAtN;
NSDate *date1 = [NSDate dateWithTimeIntervalSince1970:timestamp];
// Twitter Date Retrieval
NSDictionary *twitter = self.tweets;
NSString *twitterCreated = twitter[#"created_at"];
int createdAtTwitter = [twitterCreated intValue];
NSTimeInterval timestampTwitter = (NSTimeInterval)createdAtTwitter;
NSDate *date2 = [NSDate dateWithTimeIntervalSince1970:timestampTwitter];
return [date1 compare:date2];
};

EKEvent is not added according to given EKRecurrenceRule

I'm trying to add an event into calendar with recurrence rule RRULE:FREQ=YEARLY;BYMONTH=6,7;BYDAY=1TH
So according to this rule the event should be added yearly, each 1st thursday of june and july until expire date, which I've set in my project.
In my project, events are created but not according to the recurrence rule. With the following code the events added only on each 1st thursday of june. Why the events are not added on 1st thursday of each july also?
Here is .m file code
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self createEvent];
}
- (void)createEvent
{
EKEventStore *eventStore = [[EKEventStore alloc] init];
EKEvent *event = [EKEvent eventWithEventStore:eventStore];
event.title = #"testRecurrenceRule";
event.location = #"Dhaka";
[event setCalendar:[eventStore defaultCalendarForNewEvents]];
event.startDate = [self dateFromString:#"2013-06-18T21:00:00+06:00"];
event.endDate = [self dateFromString:#"2013-06-18T22:00:00+06:00"];
id recurrenceRule = [self recurrenceRuleForEvent];
if(recurrenceRule != nil)
[event addRecurrenceRule:recurrenceRule];
if ([eventStore respondsToSelector:#selector(requestAccessToEntityType:completion:)])
{
// iOS 6 and later
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted)
{
dispatch_async(dispatch_get_main_queue(), ^{
[self saveTheEvent:event eventStore:eventStore];
//[eventStore saveEvent:event span:EKSpanThisEvent error:error];
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
//do nothing
});
}
}];
}
else
{
[self saveTheEvent:event eventStore:eventStore];
}
textView.text = [NSString stringWithFormat:#"Event has been added with recurrence rule %#",recurrenceRule];
}
- (void)saveTheEvent:(EKEvent *)aEvent eventStore:(EKEventStore *)aStore
{
[aStore saveEvent:aEvent span:EKSpanThisEvent error:NULL];
}
- (EKRecurrenceRule *)recurrenceRuleForEvent
{
//just creating a recurrence rule for RRULE:FREQ=YEARLY;BYMONTH=6,7;BYDAY=1TH
// setting the values directly for testing purpose.
//FREQ=YEARLY
EKRecurrenceFrequency recurrenceFrequency = EKRecurrenceFrequencyYearly;
NSInteger recurrenceInterval = 1;
EKRecurrenceEnd *endRecurrence = nil;
NSMutableArray *monthsOfTheYearArray = [NSMutableArray array];
NSMutableArray *daysOfTheWeekArray = [NSMutableArray array];
NSMutableArray *daysOfTheMonthArray = [NSMutableArray array];
NSMutableArray *weeksOfTheYearArray = [NSMutableArray array];
NSMutableArray *daysOfTheYearArray = [NSMutableArray array];
NSMutableArray *setPositionsArray = [NSMutableArray array];
//BYMONTH=6,7
[monthsOfTheYearArray addObject:[NSNumber numberWithInt:6]];
[monthsOfTheYearArray addObject:[NSNumber numberWithInt:7]];
//BYDAY=1TH
[daysOfTheWeekArray addObject:[EKRecurrenceDayOfWeek dayOfWeek:5 weekNumber:1]];
endRecurrence = [EKRecurrenceEnd recurrenceEndWithEndDate:[self dateFromString:#"2018-12-15T22:30+06:00"]];
EKRecurrenceRule *recurrence = [[EKRecurrenceRule alloc] initRecurrenceWithFrequency:recurrenceFrequency
interval:recurrenceInterval
daysOfTheWeek:daysOfTheWeekArray
daysOfTheMonth:daysOfTheMonthArray
monthsOfTheYear:monthsOfTheYearArray
weeksOfTheYear:weeksOfTheYearArray
daysOfTheYear:daysOfTheYearArray
setPositions:setPositionsArray
end:endRecurrence];
return recurrence;
}
- (NSDate *)dateFromString:(NSString *)string
{
//check if the date string in null
if ([string length] == 0)
return nil;
NSString *dateString = nil;
NSString *modifiedString = nil;
BOOL secSpotMissing = false;
NSRange range = [string rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:#"T"]];
if (range.location != NSNotFound)
{
dateString = [string substringFromIndex:range.location];
range = [dateString rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:#"+-Z"]];
if (range.location != NSNotFound)
{
//seperate the time portion of date string and checking second field is missing or not. like is it HH:mm or HH:mm:ss?
if ([[[dateString substringToIndex:range.location] componentsSeparatedByString:#":"] count] < 3)
secSpotMissing = true;
//seperate the time zone portion and checking is there any extra ':' on it. It should like -0600 not -06:00. If it has that extra ':', just replacing it here.
dateString = [dateString substringFromIndex:range.location];
if([dateString hasSuffix:#"Z"])
modifiedString = [dateString stringByReplacingOccurrencesOfString:#"Z" withString:#"+0000"];
else
modifiedString = [dateString stringByReplacingOccurrencesOfString:#":" withString:#""];
string = [string stringByReplacingOccurrencesOfString:dateString withString:modifiedString];
}
}
else
return nil;
// converting the date string according to it's format.
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
if (secSpotMissing)
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mmZZZ"];
else
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZZZ"];
return [dateFormatter dateFromString:string];
}
Can somebody please help me regarding this?
This seems a repeat of another question. Basically, according to "BYDAY" rule, the 1st for YEARLY frequency means the first week in the year - instead of 1st week each month.
#Shuvo, I did not read rfc. But here is Apple document EKRecurrenceDayOfWeek.
The EKRecurrenceDayOfWeek class represents a day of the week for use with an EKRecurrenceRule object. A day of the week can optionally have a week number, indicating a specific day in the recurrence rule’s frequency. For example, a day of the week with a day value of Tuesday and a week number of 2 would represent the second Tuesday of every month in a monthly recurrence rule, and the second Tuesday of every year in a yearly recurrence rule.
When you say "1st Thursday", that is correct - except in the context of yearly, it is 1st thursday of the year.
The bug was confirmed by Apple, at least until iOS 7.1.3 (which is the latest available version at this moment).

Resources