I'm working on app that sets an UILocalNotification. This is the first time I use it and I'd like to have some explanations to use it efficiently and in the best way.
Which is the bast way to countdown to the time of notification expiration? I'd like to show to the user an UILabel with the missing time, this UILabel updates every second and when I reopen my app it must show the timer.
I've done this in this way:
When I reopen my app or the recall the view that has to show the timer I check if there is a UILocalNotification with the userInfo I've setted previously then I subtract to its NSDate
the current time so I get the difference, and update the UILabel. I know it shows time in seconds but if this procedure it's good I will convert it into time.
Is there any best way to do this?
Here is my code:
UILocalNotification *countDownNotificationSetted;
NSArray *scheduledNotificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications];
for (int i = 0; i<[scheduledNotificationArray count]; i++)
if ([[[[scheduledNotificationArray objectAtIndex:i] userInfo] objectForKey:#"ID"] isEqualToString:#"ParkNotification"]) {
countDownNotificationSetted = [scheduledNotificationArray objectAtIndex:i];
break;
}
NSDate *expringDate = [countDownNotificationSetted fireDate];
NSTimeInterval timeLeftToNotification = [expringDate timeIntervalSinceNow];
if(timeLeftToNotification == 0){
[countDownTimer invalidate];
}
datePicker.countDownDuration = timeLeftToNotification;
_countDownLabel.text = [NSString stringWithFormat:#"%.0f", timeLeftToNotification];
NSLog(#"%.0f", timeLeftToNotification);
I think it would be better if you would update your label with a timer as you already do , when you close the app, store that time locally in a file or user defaults and when the app launches again read from that file the time. Or if you want the time passed since the first openning of the app, just write the time in appDidFinishLaunching and get it from there every time . I don't see the benefit of the local notification here.
Hope this helps.
Cheers!
Related
I was wondering if there was a clean way to do a countdown timer with Grand Central Dispatch (then display for example in a UILabel) that's synchronized to the system clock... based on a reference date? — So if my reference date is an NSDate that's 20 minutes from now, I'd have a countdown displayed in seconds (don't worry about formatting) that's synced to the system clock.
Just doing a quick version of this skips seconds every once in a while in case the update method call doesn't arrive on schedule.
To me this seems like a pretty basic question, so I'm looking ideally for a basic/clean solution besides increasing the update interval to be 10x or something.
Also, the solution shouldn't use NSTimer.
I would use a recursive method like this:
- (void) updateTimer{
NSDate *now = [NSDate date];
NSTimeInterval secondsToDate = [now timeIntervalSinceDate:self.referenceDate];
_timerLabel.text = [NSString stringWithFormat:#"%.0f", secondsToDate];
if( secondsToDate < 0 ){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self updateTimer];
});
}else{
NSLog(#"Timer triggered!");
}
}
You just have to call it the first time, then it will update the countdown every 0.1 seconds.
I'm trying to figure out how I can make an alert popup once the user has reached their goal. However, I only want it to show once a day after it's been triggered. Apologies in advance. if my logic is off with tackling this issue.
// Check to see if hydration goal has been reached
if (waterCups >= dailyHydrationGoal) {
// User has reached goal
if (alert has not been fired yet today) {
// alert pop up you reached your goal!
}
}
How would I go about ensuring it only launches once per day?
You can use isDateInToday: method to check whether the given date is in “today.”
NSDate *lastAlertDate = (NSDate *)[[NSUserDefaults standardUserDefaults] objectForKey:#"lastAlertDate"];
if(![[NSCalendar currentCalendar] isDateInToday:lastAlertDate]){
//Show alert
NSDate *today= [NSDate date];
[[NSUserDefaults standardUserDefaults] setObject:today forKey:#"lastAlertDate"];
}
Do you have a persistence store? (NSUserDefaults, core data etc...)
You can store a time stamp (NSDate) of the last time an alert was shown then check if the last time it was shown is today. How to determine if an NSDate is today?
If an alert has not been shown today, pop an alert and record a new time stamp.
i have an application like timer in IOS. In the first screen i have button when i am click on it the timer is start and after some amount of time say 30 seconds i have run my application then i am stop my application (not in background) , i am terminating the application. I reopen my application after the timer shows 1 min, that is working fine but when i am changing the device system time then the timer shows different time.
NSTimeInterval oldTimeInterval = [[NSUserDefaults standardUserDefaults] doubleForKey:PunchInTimeWhenGoneBackgroundKEY];
NSTimeInterval currentTimeInterval = [[NSDate date] timeIntervalSince1970];
//if(currentTimeInterval>oldTimeInterval)
//{}
WORKING_TIME = WORKING_TIME + (currentTimeInterval - oldTimeInterval) + 1;
if(timerWorkTime!=nil)
{
[timerWorkTime invalidate];
timerWorkTime = nil;
}
timerWorkTime = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(funCalculateWorkingTime) userInfo:nil repeats:YES];
-(void)funCalculateWorkingTime
{
WORKING_TIME++;
workingHour = (int) WORKING_TIME / 3600;
WorkingMin = (int) WORKING_TIME / 60;
WorkingSecond = (int) WORKING_TIME % 60;
lblWorkingTime.text = [NSString stringWithFormat:#"%.2d : %.2d : %.2d",workingHour,WorkingMin,WorkingSecond];
earnedMoney = ((float)WORKING_TIME * HOURLY_RATE)/3600.0;
lblTotalEarned.text = [NSString stringWithFormat:#"$%.2f",earnedMoney];
// NSLog(#"time : %#",lblWorkingTime.text);
}
Please do help me.
Thanks in advance.
Don't use the system time for measurement. Store the time differences using UserDefaults or your own property list.
I don't know of a way to correct this without using a server to provide the timer functionality.
Without the app being able to run in the background, any timer that you set to save and check even when the app is not running will rely on the local device's system time. Any changes to the system time will mess that up.
My only thought would be to create a very simple server component where you could register a timer on the site, and then check with the server on startup to see what timers are available and how much time is left on them.
Edit: There is one option you could use, but it wouldn't be fool-proof. You can use CACurrentMediaTime() to get a time that is relative to the boot time. If the user changes time zones, or manually changes the system date, it would not be affected. It would still be relative to the last system boot. The down side is that it will throw off all your timers if the user restarts their phone.
I'm working with an app that processes device motion events and updates interface in 5 second increments. I would like to add an indicator to the app that would display the total time the app has been running. It seems that a stopwatch-like counter, like the native iOS Clock app is a reasonable way to count time that the app has been running and display it to the user.
What I'm not sure of is the technical implementation of such a stopwatch. Here's what I'm thinking:
if I know how long between interface updates, I can add up seconds between events and keep a count of seconds as a local variable. Alternatively, a 0.5 second interval scheduled timer can provide the count.
If I know the start date of the app, I can convert the local variable to date for each interface update using [[NSDate dateWithTimeInterval:(NSTimeInterval) sinceDate:(NSDate *)]
I can use a NSDateFormatter with a short time style to convert the updated date to a string using stringFromDate method
The resulting string can be assigned to a label in the interface.
The result is that the stopwatch is updated for each "tick" of the app.
It appears to me that this implementation is a bit too heavy and is not quite as fluid as the stopwatch app. Is there a better, more interactive way to count up time that the app has been running? Maybe there's something already provided by iOS for this purpose?
If you look in the iAd sample code from Apple in the basic banner project they have a simple timer:
NSTimer *_timer;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
and the the method they have
- (void)timerTick:(NSTimer *)timer
{
// Timers are not guaranteed to tick at the nominal rate specified, so this isn't technically accurate.
// However, this is just an example to demonstrate how to stop some ongoing activity, so we can live with that inaccuracy.
_ticks += 0.1;
double seconds = fmod(_ticks, 60.0);
double minutes = fmod(trunc(_ticks / 60.0), 60.0);
double hours = trunc(_ticks / 3600.0);
self.timerLabel.text = [NSString stringWithFormat:#"%02.0f:%02.0f:%04.1f", hours, minutes, seconds];
}
It just runs from start up, pretty basic.
Almost what #terry lewis suggested but with an algorithm tweak:
1) schedule a timer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
2) when the timer fires, get the current time (that's the tweak, don't count ticks because if there is wobble in the timer, tick counting will accumulate the error), then update the UI. Also, NSDateFormatter is a simpler and more versatile way to format time for display.
- (void)timerTick:(NSTimer *)timer {
NSDate *now = [NSDate date];
static NSDateFormatter *dateFormatter;
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"h:mm:ss a"; // very simple format "8:47:22 AM"
}
self.myTimerLabel.text = [dateFormatter stringFromDate:now];
}
I have the following code below that is meant to change a class var called "today" forward or backward by one day. It will work one time but then after that it crashes. It will do the same no matter if I press the left button or right button. What am I doing wrong?
the var today is a class var initiated as .. today = [NSDate date]
Here is the method that crashes:
(IBAction)changeDateByOne:(id)sender{
NSDate *newDay;
NSDate *currentDay = today;
NSTimeInterval secondsPerDay = 24 * 60 * 60;
if(sender == leftButton){
newDay = [currentDay addTimeInterval:-secondsPerDay];
}else if(sender == rightButton) {
newDay = [currentDay addTimeInterval: secondsPerDay];
}
today = newDay;
}
Not only do you need to retain the date created, but you also need to release the existing value held by "today," otherwise you'll leak the old reference.
When initializing the instance, use:
today = [[NSDate date] retain];
I would change the last line to:
[today release];
today = [newDay retain];
And finally, in your dealloc method, add:
[today release];
before calling [super dealloc];
You need to read the memory management documentation. That’s here:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
If you’re adding or subtracting days, you might want to read this which is an alternative way of doing the same thing:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/DatesAndTimes/Articles/dtCalendricalCalculations.html#//apple_ref/doc/uid/TP40007836-SW1
Lastly, if something crashes, it’s often helpful to look at the back traces (and include them in your questions if you can’t figure it out for yourself). Memory management bugs are usually the problem if you see objc_msgSend (or one of its companions) in the trace.
Maybe you need to say
today = [[NSDate date] retain]
I think you need to retain the newDay object returned from the addTimeInterval method. You may also need to release today before you do the assignment at the end.