NSDate comparison issues - ios

I just can't figure out what's going on here... Essentially, my code saves an object (to server) and logs the time in NSUserDefaults. The next time a user tries to save an object, I check to see if the date is in-between the stored time, and the stored time plus 2 hours. So basically, I want to prevent the user from saving an additional object within 2 hours of the other. I have the code written and finished... Here's the catch, it works 100% of the time on my end (Central Standard Time), but I have a beta tester in California (Pacific Time) that has issues with it. The times being recorded are correct, but the 2 hour windows are off, and often don't get overwritten... There's a few other places in the app where times can be updated, but the code is essentially, the exact same.
Here's the code in the "save object method":
//Combine date and time
NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components1 = [gregorianCalendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:self.pickedDate];
NSDateComponents *components2 = [gregorianCalendar components:NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:self.pickedTime];
NSDateComponents *components3 = [[NSDateComponents alloc] init];
[components3 setYear:components1.year];
[components3 setMonth:components1.month];
[components3 setDay:components1.day];
[components3 setHour:components2.hour];
[components3 setMinute:components2.minute];
[components3 setSecond:components2.second];
//Generate a new NSDate from components3.
NSDate *combinedDate = [gregorianCalendar dateFromComponents:components3];
//Create "plus 2 hour" date
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDate *time = [defaults objectForKey:#"lastObject_time"];
NSTimeInterval twoHours = 2 * 3600;
NSDate *plusTwoHours = [time dateByAddingTimeInterval:twoHours];
if (time) {
//There is a stored time
if (![self date:combinedDate isBetweenDate:time andDate:plusTwoHours]) {
//After 2 hours, allow save. After save, check if it's the LATEST date, compared to what we have stored
if ([combinedDate compare:[defaults objectForKey:#"lastObject_time"]] == NSOrderedDescending) {
//Save as most recent time
[defaults setObject:combinedDate forKey:#"lastObject_time"];
[defaults synchronize];
}
} else {
//Before 2 hours, prevent save
}
} else {
//No stored time, allow save
}
Here's my isBetweenDate method
-(BOOL)date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate {
if ([date compare:beginDate] == NSOrderedAscending)
return NO;
if ([date compare:endDate] == NSOrderedDescending)
return NO;
return YES;
}
Like I said, the times are stored according to their timezone, and I'm not thinking it's a timezone issue. My beta tester has had a few occasions of saving at 7 AM (stored time is 7 AM, blocked until 9 AM), then saves at 12 PM (which is allowed), but then the range doesn't update to 12-2 PM, and is stuck at 7-9 AM), and then they're allowed to save again at 12:01 PM Could there be a reason why it wouldn't work across timezones? Does anyone see anything that jumps out at them? Had similar experiences? Or is there an overall better way to do this?

Related

Change label every day at the same time

I'm programming a basic quote app where I want to add a "daily quote". I have a NSObject with the quotes/authors and also a function which randomises the quotes.
let range: UInt32 = UInt32(quotes.count) //my array is called quotes
let randomNumber = Int(arc4random_uniform(range))
let quote = quotes[randomNumber]
let quoteString = quote.quote
let authorString = quote.person
self.quoteLabel.text = quoteString
self.personLabel.text = authorString
How do I create a function in which the quote is only changed every day (at midnight/00:00?)
I have been trying to use NSDate and NSTimer, but understand that these will be cancelled when the app is closed. I have also been trying to implement this code Update Every Day in swift syntax - but without any results.
How do I change the quote every day at midnight and keep it there throughout the day?
I'm new to programming and would really appreciate a detailed answer with code, I have only been coding for a couple of months just - thank you!
Something like this maybe? sorry that it is objc)
- (void)checkDateAndSwitchLabel
{
//get previous Cached data when label refreshed
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger cachedDay = [defaults integerForKey:#"appDate"];
NSInteger cachedHour = [defaults integerForKey:#"appDateHour"];
//get current data
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitDay | NSCalendarUnitHour fromDate:[NSDate date]];
NSInteger currentDay = [components day];
NSInteger currentHour = [components hour];
//compare
if (cachedDay - currentDay >= 1 || cachedHour > currentHour) {
//change Label
}
//save new values
[defaults setInteger:currentDay forKey:#"appDateDay"];
[defaults setInteger:currentHour forKey:#"appDateHour"];
[defaults synchronize];
}

iOS Parse (Reduce API Requests) Perform function once a week every week on the same day

Using Parse, I'm trying to reduce the amount of network calls to parse in an effort to limit my API requests of nonessential data. To do this I thought it would be best to call a function only once a week on the same day and upload any differences from the previous week to the installation class (for syncing push notification channels in my circumstance)
So how do you call a function once a week every week on a particular day? And what do you if the day is already passed? So say you want something to happen every Thursday, but a user doesn't open the app until Sunday after that Thursday you were supposed to sync data?
In practice, I find that a fixed interval makes more sense in more cases than a calendar milestone. With that, and very little NSDate logic, I can have my model guarantee that its data is at most N seconds out of date.
To do this, I have the model singleton keep track only of the date of the last update:
// initialize this to [NSDate distantPast]
#property(nonatomic,strong) NSDate *lastUpdate;
The interface also provides an asynch update method, like:
- (void)updateWithCompletion:(void (^)(BOOL, NSError *))completion;
I override the synthesized getter/setter of lastUpdate to wrap persistence:
// user defaults in this case, but there are several ways to persist a date
- (NSDate *)lastUpdate {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
return [defaults valueForKey:#"lastUpdate"];
}
- (void)setLastUpdate:(NSDate *)lastUpdate {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:lastUpdate forKey:#"lastUpdate"];
[defaults synchronize];
}
Finally, the asynch update opaquely decides whether the current data is good enough, or whether we should invoke the parse.com api...
- (void)updateWithCompletion:(void (^)(BOOL, NSError *))completion {
NSTimeInterval sinceLastUpdate = -[self.lastUpdate timeIntervalSinceNow];
NSTimeInterval updatePeriod = [self updatePeriod];
if (sinceLastUpdate < updatePeriod) {
// our current data is new enough, so
return completion(YES, nil);
} else {
// our current data is stale, so call parse.com to update...
[...inBackgroundWithBlock:(NSArray *objects, NSError *error) {
if (!error) {
// finish the update here and persist the objects, then...
self.lastUpdate = [NSDate date];
completion(YES, nil);
} else {
completion(NO, error);
}
}];
}
}
The method updatePeriod answers whatever NSTimeInterval the app thinks is an acceptable age of the data. Usually, I get this from parse.config at some reasonably high frequency (like daily). This way, I can tune the frequency of model updates as I see fit for clients in the wild.
So with very little NSDate logic, I use this to keep the clients "up-to-date-enough", where even the "enough" part can be dynamically decided.
EDIT - we can still remain concise and set our model expiration to be a calendar day. I'd do that as follows:
- (NSDate *)lastSaturday {
NSDate *now = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *startOfWeek;
[calendar rangeOfUnit:NSCalendarUnitWeekOfMonth startDate:&startOfWeek interval:NULL forDate:now];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
return [calendar dateByAddingComponents:components toDate:startOfWeek options:0];
}
Now, instead of updating when the interval expires, update if the last update was before last Saturday (or whatever weekday, you want... adjust by adjusting setDay:-n)
// change the date condition in updateWithCompletion
if (self.lastUpdate == [self.lastUpdate earlierDate:[self lastSaturday]) {
// do the update
} else {
// no need to update
}
The first thing you want to do is check if today is the day you want the data to sync. So for instance, I want every Thursday a function to be called to sync an array of objects to the 'channels' column.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([self isTodayThursday]) {
} else {
}
}
- (BOOL) isTodayThursday {
NSDateFormatter *nameOfDayOfWeekFormatter = [[NSDateFormatter alloc] init];
[nameOfDayOfWeekFormatter setDateFormat:#"EEEE"];
if ([[nameOfDayOfWeekFormatter stringFromDate:[NSDate date]] isEqualToString:#"Thursday"]) {
return YES;
}
return NO;
}
Simple, here we are checking if the name of the day of the week is Thursday. Now we want to make sure we aren't stacking syncs and ensure we have or have not synced already today, if it is a Thursday. We do that by validating against an object in NSUserDefaults :
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([self isTodayThursday]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"lastSynced"] isEqualToString:[[self dateFormatter] stringFromDate:[NSDate date]]]) {
//So we check against a custom date formatter that we want of the last sync date we performed, and if it's already been synced today, then do nothing
} else {
//if not, sync plist (or whatever we want) to channels array
[self syncPlistToParse];
}
} else {
}
}
- (NSDateFormatter *)dateFormatter {
NSDateFormatter *formatedDate = [[NSDateFormatter alloc] init];
[formatedDate setDateFormat:#"yyyyMMdd"];
NSLog(#"Today is %#", [formatedDate stringFromDate:[NSDate date]]);
return formatedDate;
}
Sync plist to PFInstallation channels array
-(void)syncPlistToParse {
//First obtain whatever data you have from where ever you store it for the week. I chose to save all the data in a plist until the next sync date to reduce API calls.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *plistPath = [path stringByAppendingPathComponent:#"AlertSubscriptions.plist"];
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:plistPath];
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObjectsFromArray:array forKey:#"channels"];
[currentInstallation saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
if (success) {
//if successful save lastSynced date to NSUserDefaults to today
[[NSUserDefaults standardUserDefaults] setObject:[[self dateFormatter] stringFromDate:[NSDate date]] forKey:#"lastSynced"];
} else {
//It wasn't a successful save so you can do whatever you want on an unsuccessful save, but I chose to use `saveEventually` as a back-up for connectivity issues etc
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation addUniqueObjectsFromArray:array forKey:#"channels"];
[currentInstallation saveEventually];
}
}];
}
So if today is NOT Thursday how do we determine if it's before or after the lastSynced date and how do we determine how to set the next sync date?
Lets use these dates as an example:
June 2015
---------------------------------------------
|SUN | MON | TUES | WED | THURS | FRI | SAT |
---------------------------------------------
| | | | | 18 | 19 | 20 |
---------------------------------------------
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
---------------------------------------------
| 28 | 29 | 30 | 1 | 02 | 03 | 04 |
---------------------------------------------
So lets say the last synced Thursday was 18th of June and the user doesn't open their app until 26 of June (Friday) we need to validate against that. We already checked if today was a Thursday and we know it's not since it's Friday the 26th. So there are two descrepencies we need to do checks and balances on, most import, we need to check if that Friday was within the same week of the current sync cycle (the 19th Friday), but in this case since the current week cycle already passed because we didn't open it up until past the next cycle (which was supposed to be the 25th of June)
if ([self isTodayThursday]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"lastSynced"] isEqualToString:[[self dateFormatter] stringFromDate:[NSDate date]]]) {
//So we check against a custom date formatter that we want of the last sync date we performed, and if it's already been synced today, then do nothing
} else {
//if not, sync plist (or whatever we want) to channels array
[self syncPlistToParse];
}
} else {
//Not Thursday. So we need to check if its within the same week of the sync cycle or passed that date
int lastSyncDate = [[self lastSyncDate] intValue];
NSLog(#"lastSyncDate int %d", lastSyncDate);
int nextSyncDate = [[self nextSyncDate] intValue];
NSLog(#"nextSyncDate int %d", nextSyncDate);
if (lastSyncDate < nextSyncDate) {
NSLog(#"next sync date is this coming thursday");
} else {
// if not before thursday (already passed Thursday), sync and save today as lastSynced
[self syncPlistToParse];
}
}
}
- (NSString *)lastSyncDate {
NSString *date = [[NSUserDefaults standardUserDefaults] objectForKey:#"lastSynced"];
return date;
}
- (NSString *)nextSyncDate {
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *lastSync = [[self dateFormatter] dateFromString:[self lastSyncDate]];
NSDateComponents *components = [cal components:(NSCalendarUnitDay) fromDate:lastSync];
[components setDay: +[self daysUntilNearestThursday]];
NSDate *nextSync = [cal dateByAddingComponents:components toDate:lastSync options:0];
return [[self dateFormatter] stringFromDate:nextSync];
}
-(int)daysUntilNearestThursday {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"EEEE"];
//Note the day of the week is including today thats why it's always +1
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:#"Monday"]) {
return 4;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:#"Tuesday"]) {
return 3;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:#"Wednesday"]) {
return 2;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:#"Friday"]) {
return 7;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:#"Saturday"]) {
return 6;
}
if ([[dateFormatter stringFromDate:[NSDate date]] isEqualToString:#"Sunday"]) {
return 5;
}
return 0;
}
NOTE It doesn't matter what the nextSyncDate is because we always check if it's Thursday first and then act accordingly
This is my personal opinion, someone asked me about this and this is what I came up with. I feel like theres an easier solution out there that accurately resets the nextSync to the nearest Thursday instead of [components setDay: +[self daysUntilNearestThursday]]; So by all means, please add input/suggestions and comments to make this a better opportunity for future question seekers. I think Parse is fantastic, but at the same time it's a business, and they have to act accordingly, but certain things like API requests for everything ever for a scaleable app (2,000+ users) is kind of bad practice if you intend on keeping developers. This is an alternative I thought of to help my friend out, it needs work, not saying its the only way, just the way I thought would work for their project. Please, don't be shy, add edits or corrections.

My NSDate countdown timer changes when I change views

I have a countdown timer from the current date until 10 minutes have passed.
It counts down completely fine when I'm on the view, but when I change views and come back, then the timer stops, could anybody point me in the right direction?
-(void)updateCountdown {
dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:#"YYYY-MM-dd-HH-mm-ss"];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSUInteger unitFlags = NSMinuteCalendarUnit|NSSecondCalendarUnit;
NSDateComponents *dateComponants = [calendar components:unitFlags fromDate:startingDate toDate:endingDate options:0];
NSInteger minutes = [dateComponants minute];
NSInteger seconds = [dateComponants second];
NSString *countdownText = [NSString stringWithFormat:#"Free Check: %ld:%ld", (long)minutes, (long)seconds];
timeLabel.text = countdownText;
NSLog (#"Startdate is %#", startingDate);
NSLog(#"Enddate is %#", endingDate);
//Attempt at saving the time but I guess this only saves the text?
NSString * saveStringTime = timeLabel.text;
NSUserDefaults * defaultsTime = [NSUserDefaults standardUserDefaults];
[defaultsTime setObject:saveStringTime forKey:#"saveStringTime"];
[defaultsTime synchronize];
[self performSelector:#selector(updateCountdown) withObject:nil afterDelay:1];
}
I think I need something in my ViewDidLoad to get whatever time it was and put that back in?
Thank you
You have to get the "savedStringTime" in viewWillAppear to resume from the "savedTime". Since viewDidLoad will get invoked only if the view is loaded to memory, viewDidLoad will not get invoked when the user navigates back to the same view(since view is still in memory).
To get proper working, Invoke your method to save the time in "viewWillDisappear" and in "viewWillAppear" check if the "savedStringTime" is available, if yes then resume it or else start a fresh counter.

Load an image on specific date and time in iOS [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
My question is: how do I load a specific image on a specific date and time in iOS?
I have searched the net but did not find anything useful.
I have a list of images in an imageArray and want every image to be shown on a specific date, time and order.
Fx. say I want to load MyImage on MyDateAndTime. How can I do this?
Image 1 - DateAndTime 1
Image 2 - DateAndTime 2
Image 3 - DateAndTime 3
Any suggestions is appreciated, please provide some source code if possible.
I put simple logic, edit it as per your requirement otherwise if you have any query related to my answer then please tells to me.
Best way is store your image with Name of dateTime (dd_MM_yyyy_HH_mm_ss) and access image name such like,
NSString *imageName
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd_MM_yyyy_HH_mm_ss"];
imageName = [NSString stringWithFormat:#"%#.png", [formatter stringFromDate:[NSDate date]]]; // here you can set specific dateTime, i putted current dateTime
Here you get imageName such like 19_10_2013_6_13_21.png
And by this image name you can get image from bundle or document directory.
If all you want is to show a different image every minute, use this, otherwise skip below to see helpful date information.
NSTimeInterval secondsInMinute = 60;
[NSTimer timerWithTimeInterval:secondsInMinute target:self selector:#selector(minuteChanged:) userInfo:nil repeats:YES];
- (void)minuteChanged:(id)sender {
// change image here
}
You question could have many different answers, do you want to create this date dynamically? or is it a a predefined date? One solution is to get get the timeInterval of the date you are looking for.
NSDate* rightNow = [NSDate date];
NSTimeInterval timeInterval = [rightNow timeIntervalSince1970];
// since time intervals are in seconds we can just append the
// date as easily as adding time
NSInteger secondsInMinute = 60;
NSInteger minutesInHour = 60;
NSInteger hoursInDay = 24;
NSInteger daysInWeek = 7;
NSInteger secondsInWeek = secondsInMinute * minutesInHour * hoursInDay * daysInWeek;
timeInterval = timeInterval + secondsInWeek;
NSDate* aWeekInFuture = [NSDate dateWithTimeIntervalSince1970:timeInterval];
that i would say is the easiest to under stand to set a date, but you could also use components to set a future date dynamically. This leads into some problems but here is how it's done.
NSDate* rightNow = [NSDate date];
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* dateCompenents = [calendar components:(NSDayCalendarUnit | NSWeekCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:rightNow];
[dateCompenents setDay:dateCompenents.day + 7];
NSDate* aWeekInFuture = [calendar dateFromComponents:dateCompenents];
to help explain this, here is some console logs
(lldb) po rightNow
$0 = 0x0b933440 2013-10-19 12:43:55 +0000
(lldb) po aWeekInFuture
$1 = 0x0ba32a60 2013-10-26 04:00:00 +0000
you see how the date is accurate for the day, year, month, but look at the exact time, the current time (right now) is 12:43:55 but the week in he future is 4:00:00 this is because i did not ask for the NSMinutesCalendarUnit, NSHoursCalendarUnit, NSSecondsCalendarUnit... so if i wanted a perfect date that would be inadequate unless i ask for every single thing, but you specifically may not need to be so accurate in fact you may even want to set your own time.
Now if you want a static date, a date the user enters, you will need to use NSDateFormatter example below
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setCalendar:currentCalendar];
[dateFormatter setDateFormat:#"mm/dd/yyyy"];
NSDate* birthdayDate = [dateFormatter dateFromString:#"10/05/2013"];
Now you wanted to know how would you know if today is the specified date that is saved. Lets say you stored the date in NSUserDefaults or on a server or some place, the easiest way to compare the dates is with the compare function of an NSDate
NSDate* rightNow = [NSDate date];
NSDate* storedDate = [[NSUserDefaults standardUserDefaults] objectForKey#"storedDate"] // some date from server, or UserDefaults
NSComparisonResult = [rightNow compare:storedDate];
this is a bit inadequate since it test for perfection but it will return values of NSOrderedSame if they are equal, NSOrderedDescending if storedDate is behind rightNow, and NSOrderedAscending if storedDate is in front of rightNow. This is all specific down to the time interval. If you just want a generic day, you will have to test it via components
NSDate* rightNow = [NSDate date];
NSDate* birthdayDate = [[NSUserDefaults standardUserDefaults] objectForKey#"birthday"]
NSDateComponents* todayComponents = [currentCalendar components:(NSDayCalendarUnit | NSMonthCalendarUnit) fromDate:rightNow];
NSDateComponents* birthdayComponents = [currentCalendar components:(NSDayCalendarUnit | NSMonthCalendarUnit) fromDate:birthdayDate];
BOOL dayIsTheSame = ( todayComponents.day == birthdayComponents.day );
BOOL monthIsTheSame = ( todayComponents.month == birthdayComponents.month );
BOOL todayIsBirthday = ( dayIsTheSame && monthIsTheSame );
if (todayIsBirthday) {
[self.imgViewBirthday setImage[UIImage imageNamed:#"cake.png"]];
}
In your question you specified an array of images, lets say you have a different image depending on which hour it is, or which minute, you would use the component, todayComponent.minute after asking for the NSMinutesCalendarUnit as the index of this array;
UIImage* currentImageToDisplay = [self.arrayOfImage objectAtIndex:todayComponent.minute];
self.imageView.image = currentImageToDisplay;
References:
NSDate,
NSDateFormatter,
NSDateComponents,
NSCalendar,
NSTimer
If I understood your problem now, one fancy approach using a recursive block you might check out is this:
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
NSArray* dates = #[#1, #1, #1, #1, #1, #1, #1, #1, #1];
NSArray* urls = #[#"A", #"B", #"C", #"D", #"E", #"F", #"G", #"H", #"I"];
NSEnumerator* dateIter = [dates objectEnumerator];
NSEnumerator* urlIter = [urls objectEnumerator];
typedef void(^block_t)(NSEnumerator* dateIter, NSEnumerator* urlIter);
block_t asyncFunc;
__block __weak block_t _asyncFunc = asyncFunc = ^(NSEnumerator* dateIter, NSEnumerator* urlIter) {
NSNumber* date = [dateIter nextObject];
NSString* url = [urlIter nextObject];
if (date != nil && url != nil) {
double delayInSeconds = [date doubleValue];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_global_queue(0, 0), ^(void){
NSLog(#"%#", url);
_asyncFunc(dateIter, urlIter);
});
}
else {
printf("\n");
return;
}
};
// start:
asyncFunc(dateIter, urlIter);
sleep (10);
}
return 0;
}
Note:
The "dates" are actually "delays" and the URLs are actually just strings in this example. You should be able to adjust this as you like. Of course, NSLog(#"%#", url); would actually display your image.
Also, the block asyncFunc is asynchronous!

iOS: Tapku calendar library - allow selecting multiple dates for current month

I am using Tapku library for calendar implementation. I could see that there is a way to add Markers for predefined start and end date but I want to allow users to select/unselect any number of dates from current month only, and want to generate event for each action.
Moreover, I have switched off the month navigation functionality by returning nil for Left and Right arrow to display only current month but not able to remove events for few previous and next months Date tiles that gets displayed on current month. I can still select previous month's day 31st to navigate to previous month or select 1st on next month to navigate to next month. Can I restrict the date selection to only current month please?
Thanks.
The touches are handled in TKCalendarMonthView.m in the following method:
- (void) reactToTouch:(UITouch*)touch down:(BOOL)down
look at the block at row 563:
if(portion == 1)
{
selectedDay = day;
selectedPortion = portion;
[target performSelector:action withObject:[NSArray arrayWithObject:[NSNumber numberWithInt:day]]];
}
else if(down)
{
// this is the important part for you.
// ignore it by adding a return here (or remove the following three lines)
return;
[target performSelector:action withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:day],[NSNumber numberWithInt:portion],nil]];
selectedDay = day;
selectedPortion = portion;
}
The selecting/deselecting perhaps doesn't work as you expect. It's not like setDateSelected and setDateDeselected.. instead there is a single UIImageView*, which represents the selected state. And that view is moved around to the current position. You can search for self.selectedImageView in the code to see, what is happening.
So its not that easy to introduce multiple-date-selection. The architecture isn't built for that.
In TKCalendarMonthView there is a method name
-(void) reactToTouch:(UITouch*)touch down:(BOOL)down
in that method comment this line
[target performSelector:action withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:day],[NSNumber numberWithInt:portion],nil]];
this wont allow you to change month.
You can store all selected date in an array and pass all the values in
- (NSArray*)calendarMonthView:(TKCalendarMonthView *)monthView marksFromDate:(NSDate *)startDate toDate:(NSDate *)lastDate
The above method is used to put tiles but if u want selection image then u can replace it with tile image
You can also try this code:
You can do this by first entering the dates in to an array. code for this is.
- (void)calendarMonthView:(TKCalendarMonthView *)monthView didSelectDate:(NSDate *)d {
NSLog(#"selected Date IS - %#",inDate);
[myArray addObject:d];
for (id entry in myArray)
{
if (inDate == nil && outDate == nil)
{
inDate = d;
outDate = d;
}
if ([d compare:inDate] == NSOrderedAscending)
{
inDate = d;
}
if ([d compare:outDate] == NSOrderedDescending)
{
outDate = d;
}
d = nil;
}
}
After this you have to use a button click action by which you can make the dates selected between these two dates. Code for it is:
- (IBAction)goBtn:(id)sender
{
NSLog(#"startDate is: %#",inDate);
NSLog(#"endDate is: %#",outDate);
[calendar reload];
inDate = nil;
outDate = nil;
}
}
Then in one delegate method you just have to make an array containing all the dates between these two dates. It will be called just after the button click. Code for it is:
- (NSArray*)calendarMonthView:(TKCalendarMonthView *)monthView marksFromDate:(NSDate *)startDate toDate:(NSDate *)lastDate {
//***********
NSMutableArray *tempData = [[NSMutableArray alloc] init];
NSDate *nextDate;
for ( nextDate = inDate ; [nextDate compare:outDate] < 0 ; nextDate = [nextDate addTimeInterval:24*60*60] ) {
// use date
NSLog(#"%#",nextDate);
[tempData addObject:[NSString stringWithFormat:#"%#",nextDate]];
}
[tempData addObject:[NSString stringWithFormat:#"%#",outDate]];
//***********
NSMutableArray *marks = [NSMutableArray array];
NSCalendar *cal = [NSCalendar currentCalendar];
[cal setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDateComponents *comp = [cal components:(NSMonthCalendarUnit | NSMinuteCalendarUnit | NSYearCalendarUnit |
NSDayCalendarUnit | NSWeekdayCalendarUnit | NSHourCalendarUnit | NSSecondCalendarUnit)
fromDate:startDate];
NSDate *d = [cal dateFromComponents:comp];
NSDateComponents *offsetComponents = [[NSDateComponents alloc] init];
[offsetComponents setDay:1];
while (YES) {
if ([d compare:lastDate] == NSOrderedDescending) {
break;
}
if ([tempData containsObject:[d description]]) {
[marks addObject:[NSNumber numberWithBool:YES]];
} else {
[marks addObject:[NSNumber numberWithBool:NO]];
}
d = [cal dateByAddingComponents:offsetComponents toDate:d options:0];
}
return [NSArray arrayWithArray:marks];
}
I hope, this helped you. Please let me know if you face any problem.

Resources