I have a timer that I start and stop multiple times. How do I accumulate the values once the timer has stopped each time?
When the timer is started note the start time. Whenever the timer fires add the difference between the current time and the start time to your total. Then reset the start time to the current time. When the timer is stopped, you either add the difference between the current time and start time to the total or don't depending on your application, but you don't reset the start time. When the timer is restarted, don't add anything to the total, but reset the start time.
So something like this:
startTime = [[NSDate alloc] initWithTimeIntervalSinceNow]; // This is an NSDate* in your class
Then when your timer fires:
NSDate* currentTime = [NSDate dateWithTimeIntervalSinceNow];
accumulatedTime += [currentTime timeIntervalSinceDate:startTime]; // accumulatedTime is an NSTimeInterval in your class
[startTime release];
startTime = [currentTime retain];
etc.
I figured it out... here is how I did it:
NSTimeInterval *accumulatedTime;
accumulatedTime += 1.0; // add one second
This is assuming of course that you have the supporting code to start, pause and stop the timer.
Related
I am creating a game in Spritekit, and I am trying to set up my game in a way that when a player loses all of their lives they have to wait 30 minutes for one of their lives to be restored so that they can play again. I tried using NSTimer to do this but I figured UINotification will be more effective since I want this timer to run whether or not the app is terminated, in the background, being used or not being used. I'm having problems setting this up though.
I have the following code written thus far, when the user reaches the GameOverScene
-(instancetype)initWithSize:(CGSize)size {
if (GameLives < 5 ) {
alarm = [[UILocalNotification alloc] init];
alarm.fireDate = [NSDate dateWithTimeIntervalSinceNow:thirtyNewMinutes];
alarm.timeZone = [NSTimeZone localTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:alarm];
alarm.repeatInterval = NSCalendarUnitHour;
NSLog(#"AlarmFireDate = %#", alarm.fireDate);
}
}
The alarm.firedate shows up correctly in the NSLog when I reach the GameOverScene but when I close down my app and restart it, it shows up as null in my view controllers and never fires. How do I get my app to automatically update the user's lives in the background once the notification is scheduled regardless of whether the user is using the app or not? Should it be run in my app delegate?
Should some type of NSDate comparison like the one below run somewhere?
if ([[NSDate date] compare:allarm.fireDate] == NSOrderedDescending) {
GameLives = GameLives + 1;
NSLog(#"SUCCESS");
NSLog(#"COUNT = %lu", (long)GameLives);
}
else if ([[NSDate date] compare:allarm.fireDate] == NSOrderedAscending){
GameLives = GameLives + 1;
NSLog(#"FAILURE");
NSLog(#"COUNT = %lu", (long)GameLives);
}
else if ([[NSDate date] compare:allarm.fireDate] == NSOrderedSame){
NSLog(#"SAME");
NSLog(#"COUNT = %lu", (long)GameLives);
}
I'd be most grateful to anybody that can offer help.
EDIT: RESPONSE TO THE ANSWERS BELOW
I wrote the following code for the NSTimer and the timer starts when the game reaches the GameOver Scene.
-(void)restoreLives{
thirtyNewMinutes = 60 * 30;
update = [NSDate dateWithTimeIntervalSinceNow:thirtyNewMinutes];
if ([[NSDate date] compare:update] == NSOrderedDescending) {
NSLog(#"date1 is later than date2");
NSLog(#"SUCCESS");
NSLog(#"CurrentDate: %#", [NSDate date]);
// LifeText = #"Restored";
GameLives = GameLives + 1;
NSLog(#"LIVES = %ld", (long)GameLives);
// NSLog(#"Level 2 HighScore, %d", _Level1HighScoreNumber);
} else if ([[NSDate date] compare:update] == NSOrderedAscending) {
NSLog(#"date1 is earlier than date2");
NSLog(#"FAILURE");
NSLog(#"CurrentDate: %#", [NSDate date]);
NSLog(#"LIVES = %ld", (long)GameLives);
// Lives = 5;
// NSLog(#"dates are the same");
}
if (GameLives < 4){
[LifeTimer invalidate];
}
And then I created an NSTimer to run the method.
-(void)CheckTime{
LifeTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(restoreLives) userInfo:nil repeats:YES];
}
How would I get it to save the target time that you're speaking of?
And, hopefully I'm not overthinking this but from another perspective if I wanted to compare the current NSDate with the [NSDate dateWithTimeIntervalSinceNow:thirtyNewMinutes]; wouldn't I need to save the original date of [NSDate dateWithTimeIntervalSinceNow:thirtyNewMinutes]; when it was originally called so that if the app terminates and the timer runs the code again it compares it to the original time the code was called and doesn't reset the NSDate and compare it to 30 minutes from the time the user restarts the app and the timer begins again.
i.e. 7:15
NSDate comparison to update = [NSDate dateWithTimeIntervalSinceNow:thirtyMinutes];
is called. And timer is set to update lives at 7:45.
7:30 User terminates their app and restarts it at 7:35
When the NSTimer runs again won't it reset the time to be 30 minutes from 7:35 since it's 30 minutes from now? If this is the case how would I go about saving the original date? Please let me know, keep in mind I'm still a beginner with Objective C
A local notification works well if you want to inform the user of something, and can have a payload which you could use to keep track of the information you need, but it's probably not the best solution for you to do your timing work. If the user disables notifications for your app, it would break your functionality.
Instead, when it comes to keeping track of events based on a time, it's best to rely on date comparisons along with timers.
While your app is open, you should use an NSTimer to trigger what you need to do, which I think you have covered.
When you app goes to the background or terminates you should save the target time in some kind of persistent storage (NSUserDefaults, for example). When you app is relaunched or returns from the background, you should compare against that date and either start up the timer or trigger your code that the timer would fire yourself.
Try this to save/restore the date:
// Save the date with the key "NextFireDate"
[[NSUserDefaults standardUserDefaults] setObject:nextFireDate forKey:#"NextFireDate"];
// This forces the values to be saved by the system, which normally only happens periodically
[NSUserDefaults standardUserDefaults] synchronize];
...
// Retrieve the date with the key "NextFireDate"
NSDate *nextFireDate = [[NSUserDefaults standardUserDefaults] objectForKey:#"NextFireDate"];
You'd call the first whenever you go to the background/terminate (also invalidate your current timer) and the second when you finish launching or return from the background (and start a new timer with the retrieved date). NSUserDefaults is basically just a dictionary (that can accept scalars without having to box them yourself) that persists as long as your app is installed.
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];
}
while(...condition...)
{
//do something
NSDate *date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970];
[NSThread sleepForTimeInterval:0.2];
date = [NSDate date];
NSTimeInterval milliseconds1 = [date timeIntervalSince1970];
NSLog(#"**** time taken : %f",milliseconds1-milliseconds);
//calling some method
}
After 2 minutes of execution of this loop, the "time taken" increases from 200ms to 10s. Why? What is the problem ?
Assuming you are running in a multithreaded environment, answering your question would require a lot more information than you are giving us. Suffice to say, there is no guarantee that your sleeping thread will run "calling some method" exactly 200ms later, because that depends on what your other threads are doing.
Sleeping a thread like your example is generally considered a bad idea. Here's another way to accomplish what I think you are trying to do, but with ... better citizenship.
- (void)loopIfNeeded
{
if (...condition...) {
// do something
// ... and then call -someMethod ~200ms later
[self performSelector:#selector(someMethod) withObject:nil afterDelay:0.2];
}
}
- (void)someMethod
{
// whatever some method does
[self loopIfNeeded]; // continue loop
}
Finally, some observations:
-[NSDate timeIntervalSince1970] returns seconds (and fractions thereof, docs here), not milliseconds (although it has ms precision).
For timing, I find CFAbsoluteTime to be lighter weight:
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
// do your thing
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsed = start - end; // or just NSLog(#"elapsed %f", start - end);
The general comments being made suggesting this is not the way to pause your loop are correct, you should look at other ways to do this - using blocks, GCD and routines such as dispatch_after is one approach.
However, to answer your actual question: sleeping is imprecise, you sleep for at least the time specified but maybe longer. E.g. from the sleep(3) manual page:
System activity may lengthen the sleep by an indeterminate amount.
Having said that, extend 0.2s to 10s does seem quite a long stretch. Look for other activity that might be kicking in and slowing you down, if you are on Mavericks consider its aggressive actions (app nap, timer coalescing, etc.).
But really, unless this is just test code look at partitioning it into A: test condition do part 1 and B: do part 2 then call A, or some other way, and use a timer callback or GCD to handle the pause.
HTH
Your code is wrong!!!
while(...condition...)
{
//do something
NSDate *date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970];
[NSThread sleepForTimeInterval:0.2];
date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970]; // here should be milliseconds1
NSLog(#"**** time taken : %f",milliseconds1-milliseconds);
//calling some method
}
You're messing up your variables milliseconds and milliseconds1.
I write a simple application for mac osx that works with system time to calculate some data, i want to run an specific routine every time clock come to 12:00
Is there any system api for this?
How i do this?
actually i have a status bar item in my app that show current day in month in any selected calendar like gregorian and islamic, and i want to this value will be sync with system time
If your routine is the method - routine:(NSTimer *)timer of object:
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate]; // Current time
NSTimeInterval then = ((int)now + 86400) % 86400; // Next 12am
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:then];
NSTimer *timer = [[NSTimer alloc] initWithFireDate:date interval:86400 target:object #selector(routine:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
I'm looking to create a countdown timer for SMPTE Timecode (HH:MM:SS:FF) on iOS. Basically, it's just a countdown timer with a resolution of 33.33333ms. I'm not so sure NSTimer is accurate enough to be counted on to fire events to create this timer. I would like to fire an event or call a piece of code every time this timer increments/decrements.
I'm new to Objective-C so I'm looking for wisdom from the community. Someone has suggested the CADisplayLink class, looking for some expert advice.
Try CADisplayLink. It fires at the refresh rate (60 fps).
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(timerFired:)];
displayLink.frameInterval = 2;
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
This will fire every 2 frames, which is 30 times per seconds, which seems to be what you are after.
Note, that this is tied to video frame processing, so you need to do your work in the callback very quickly.
You basically have no guarantees with either NSTimer or dispatch_after; they schedule code to triggered on the main thread, but if something else takes a long time to execute and blocks the main thread, your timer won't fire.
That said, you can easily avoid blocking the main thread (use only asynchronous I/O) and things should be pretty good.
You don't say exactly what you need to do in the timer code, but if all you need to do is display a countdown, you should be fine as long as you compute the SMPTE time based on the system time, and not the number of seconds you think should have elapsed based on your timer interval. If you do that, you will almost certainly drift and get out of sync with the actual time. Instead, note your start time and then do all the math based on that:
// Setup
timerStartDate = [[NSDate alloc] init];
[NSTimer scheduledTimer...
- (void)timerDidFire:(NSTimer *)timer
{
NSTImeInterval elapsed = [timerStartDate timeIntervalSinceNow];
NSString *smtpeCode = [self formatSMTPEFromMilliseconds:elapsed];
self.label.text = smtpeCode;
}
Now you will display the correct time code no matter how often the timer is fired. (If the timer doesn't fire often enough, the timer won't update, but when it updates it will be accurate. It will never get out of sync.)
If you use CADisplayLink, your method will be called as fast as the display updates. In other words, as fast as it would be useful, but no faster. If you're displaying the time, that's probably the way to go.
If you are targeting iOS 4+, you can use Grand Central Dispatch:
// Set the time, '33333333' nanoseconds in the future (33.333333ms)
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333);
// Schedule our code to run
dispatch_after(time, dispatch_get_main_queue(), ^{
// your code to run here...
});
This will call that code after 33.333333ms. If is this going to be a loop sorta deal, you may want to use the dispatch_after_f function instead that uses a function pointer instead of a block:
void DoWork(void *context);
void ScheduleWork() {
// Set the time, '33333333' nanoseconds in the future (33.333333ms)
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333);
// Schedule our 'DoWork' function to run
// Here I pass in NULL for the 'context', whatever you set that to will
// get passed to the DoWork function
dispatch_after_f(time, dispatch_get_main_queue(), NULL, &DoWork);
}
void DoWork(void *context) {
// ...
// Do your work here, updating an on screen counter or something
// ...
// Schedule our DoWork function again, maybe add an if statement
// so it eventually stops
ScheduleWork();
}
And then just call ScheduleWork(); when you want to start the timer. For a repeating loop, I personally think this is a little cleaner than the block method above, but for a one time task I definitely prefer the block method.
See the Grand Central Dispatch docs for more info.