NSTimer and overlapping laps - objective-c

I have an NSTimer that acts for the basis of a stopwatch.
- (void)startTimer
{
_startDate = [NSDate date];
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0
target:self
selector:#selector(updateTimer)
userInfo:nil
repeats:YES];
}
I need to record lap times, but I am running into the issue that 2 separate lap times should not share any overlap.
i.e if lap 1 starts at 0.0 and ends at 10.0, then lap 2 should start at 10.01
But at the moment when there is lap I just take the current time, to work out both the end of the previous lap and the start of the new one:
- (void)lap
{
NSTimeInterval timeInterval = [[NSDate date] timeIntervalSinceDate:_startDate];
timeInterval += _timeElapsed;
_startDate = [NSDate date];
}
I need to save the start/end times of each lap as well as their total length. But I cannot see how to do this without having the start/end times be the same for different laps.
Has anyone run into this issue? Any ideas how I can solve it sensibly. Everything I think of feels very brittle.

If it is a true lap timer, as I understand, then your assumption that two separate laps should have a gap in them ins not accurate. Specifically, the statement:
i.e if lap 1 starts at 0.0 and ends at 10.0, then lap 2 should start
at 10.01
is incorrect for my understanding of a lap timer. The second lap starts immediately, as the start of lap2 is the same as the stop of lap1. Otherwise, you are losing time with those gaps.
I suggest saving only each individual time. So, if you went 5 laps, there should be size times (five if you always start at zero). You now have a time marking for the begin, and end of each lap.
0.00
10.03
20.04
30.09
40.20
50.33
The total time is 50.53, and the time for the third lap is 10.05 (30.09 - 20.04). The sum of all individual laps should equal the total time.
Now, you easily have the total time and the time for each lap. Pretend these values were stored in an array, you could easily do:
- (void)startStopLap {
// compute elapsed time...
[self.times addObject:[NSNumber numberWithDouble:elapsedTime]];
}
- (double)totalTime {
[[self.times lastObject] doubleValue]
- [[self.times objectAtIndex:0] doubleValue];
}
- (double)timeForLap:(NSUInteger)lap {
// Example assumes first lap is lap '1' and that you do
// appropriate error checking...
return [[self.times objectAtIndex:lap] doubleValue]
- [[self.times objectAtIndex:lap-1] doubleValue];
}
Similarly, you can get the time for the first N laps, with simple subtraction, and you could get the times for specific ranges of laps (i.e., laps 2-4).
Hopefully, that makes sense, and I don't have too many typos without a compiler to check...

Related

Accuracy of NSTimer

I am trying to use NSTimer to create a Stop-watch style timer that increments every 0.1 seconds, but it seems to be running too fast sometimes ..
This is how I've done it:
Timer =[NSTimer scheduledTimerWithTimeInterval: 0.1 target:self selector:#selector(updateTimeLabel) userInfo:nil repeats: YES];
and then:
-(void)updateTimeLabel
{
maxTime=maxTime+0.1;
timerLabel.text =[NSString stringWithFormat:#"%.1f Seconds",maxTime];
}
This will display the value of the timer in the Label, and I can later utilize maxTime as the time when the Timer is stopped ...
THe problem is that it runs very inaccurately.
Is there a method where I can make sure that NSTimer fires strictly every 0.1 seconds accurately ? I know that NSTimer isn't accurate , and I'm asking for a tweak to make it accurate.
THanks
According to the NSTimer documentation, it is not meant to be accurate.
Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds. If a timer’s firing time occurs during a long callout or while the run loop is in a mode that is not monitoring the timer, the timer does not fire until the next time the run loop checks the timer. Therefore, the actual time at which the timer fires potentially can be a significant period of time after the scheduled firing time.
You may want to use the dispatch_after function from GCD, which is suggested by the official documentation for this exact purpose (creating a timer).
If you want to perform a block once after a specified time interval, you can use the dispatch_after or dispatch_after_f function.
By the way, I agree with Caleb's answer. You probably are going to solve your problems if you don't accumulate error like your doing right now.
If you store the start date and recalculate the time at every iteration using the -timeIntervalSince: method, you're gonna end up with an accurate UI update, regardless of the timer precision.
Here's a class you can use to do what you want:
#interface StopWatch()
#property ( nonatomic, strong ) NSTimer * displayTimer ;
#property ( nonatomic ) CFAbsoluteTime startTime ;
#end
#implementation StopWatch
-(void)dealloc
{
[ self.displayTimer invalidate ] ;
}
-(void)startTimer
{
self.startTime = CFAbsoluteTimeGetCurrent() ;
self.displayTimer = [ NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector( timerFired: ) userInfo:nil repeats:YES ] ;
}
-(void)stopTimer
{
[ self.displayTimer invalidate ] ;
self.displayTimer = nil ;
CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ;
[ self updateDisplay:elapsedTime ] ;
}
-(void)timerFired:(NSTimer*)timer
{
CFAbsoluteTime elapsedTime = CFAbsoluteTimeGetCurrent() - self.startTime ;
[ self updateDisplay:elapsedTime ] ;
}
-(void)updateDisplay:(CFAbsoluteTime)elapsedTime
{
// update your label here
}
#end
The key points are:
do your timing by saving the system time when the stop watch is started into a variable.
when the the stop watch is stopped, calculate the elapsed time by subtracting the stop watch start time from the current time
update your display using your timer. It doesn't matter if your timer is accurate or not for this. If you are trying to guarantee display updates at least every 0.1s, you can try setting your timer interval to 1/2 the minimum update time (0.05s).
maxTime=maxTime+0.1;
This is the wrong way to go. You don't want to use a timer to accumulate the elapsed time because you'll be accumulating error along with it. Use the timer to periodically trigger a method that calculates the elapsed time using NSDate, and then updates the display. So, change your code to do something instead:
maxTime = [[NSDate date] timeIntervalSince:startDate];
NSTimer is not guaranteed to be accurate, although in practice it usually is (if you're not doing anything else on your main thread...). However, it's perfectly reasonable for updating a display... just don't use the callback to calculate your timer. Save the current time when you start your timer, and get the difference between now and when you started every time the timer fires. Then it doesn't really matter how accurately NSTimer is firing, it only impacts how many times a second your on screen display updates.

Time elapsed when battery increased of 5%

I would like to know if there's a way to get the time elapsed when the battery increased of 5%. For example, how can I know how much time elapsed between 60% and 65% ? I think I could do this with NSTimer, but I'm not able to do this, can someone help me ?
Thanks a lot.
If you are doing this for a Mac, please check this question for how to get battery life in Mac;
If you are doing this for iOS, please check this question for how to get battery life in iOS.
Simply use your NSTimer to fire the function to get the battery life every x seconds and when it gets to 60%, capture a timestamp with NSDate, then when it gets to 65%, capture another timestamp and compare the two timestamps to get the time difference: SO question: how to get time between 2 NSDate objects.
Good luck.
EDIT:
All the methods to get the battery percentage are in either the first or second link based on your platform. If you want it to determine the time between now, and 5% up/down:
//both percent and date should be properties or instance variables (NSDate and float, respectively)
//You should probably also make the timer one as well, so you can stop it in any method with [tmr invalidate];
date = [NSDate date];
percent = [self getBatteryPercent];
NSTimer* tmr = [NSTimer scheduledTimerWithInterval:1 target:self selector:#selector(someMethod) userInfo:nil repeats:YES];
- (float)getBatteryPercent
{
//You'll have to get this code from one of those methods (first or second link)
}
- (void)someMethod
{
float newPercent = [self getBatteryPercent];
if(percent - newPercent == 5.0 || percent - newPercent == -5.0)
{
//Get the time between timestamps
NSDate* newDate = [NSDate date];
//See third link for how to get the time difference between date and newDate
}
}
The rest is up to you.

How do I count time in iOS?

I don't want to set up a timer that "fires" (and does something) after a certain amount of time has passed.
Therefore, I'm not interested in the NSTimer class and its methods.
All I'm interested in is counting the time that passes by while, for example, some while-loop is executing. I could use NSDate as follows I guess :
NSDate currentDate = [[NSDate alloc] init];
while(someConditionIsTrue)
{
// executing..
}
timeElapsed = [self timeIntervalSinceDate:currentDate];
NSLog(#"time elapsed was: %i", timeElapsed);
However, if I understand correctly, timeIntervalSinceDate: can only count seconds.
Is there a way I can count the time that is passing by in milliseconds?
In other words, what is the smallest unit I can count passing time in and how ?
Look at CFAbsoluteTimeGetCurrent()
CFAbsoluteTime before = CFAbsoluteTimeGetCurrent();
CFAbsoluteTime after = CFAbsoluteTimeGetCurrent();
Your second approach is correct. Save the current date in an NSDate object and use timeIntervalSinceDate: to get the passed time since then. The result will be of type NSTimeInterval which is a floating point number. It specifies time differences in seconds, but since it's a floating point number it can store fractions of a second as well.
NSTimeInterval is always specified in seconds; it yields
sub-millisecond precision over a range of 10,000 years.

Display a countdown to a particular date, in day/moth/year format

In my iPhone app I want to have a UILabel which counts down to a certain date.
So far I have the user select a date from a UIDatePicker, but I can't figure out how to actually logically countdown each unit of the date. It needs to be live as well, so you can watch the date change rather than having to reload the view and see the difference from now until then.
I want to have a format like so (might change it up later, not sure):
dd / mm / yyyy
You will want an NSTimer to fire every time you wish to update the label and then you need to use NSDate's timeIntervalSinceNow method to get the time interval between your date and now. Then use that to update the label.
For instance:
- (void)startTimer {
...
// Set the date you want to count from
self.countdownDate = [NSDate date...]; ///< Get this however you need
// Create a timer that fires every second repeatedly and save it in an ivar
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateLabel) userInfo:nil repeats:YES];
...
}
- (void)updateLabel {
NSTimeInterval timeInterval = [self.countdownDate timeIntervalSinceNow]; ///< Assuming this is in the future for now.
/**
* Work out the number of days, months, years, hours, minutes, seconds from timeInterval.
*/
self.countdownLabel.text = [NSString stringWithFormat:#"%#/%#/%# %#:%#:%#", days, months, years, hours, minutes, seconds];
}
To get the actual number of days, months, years, hours, minutes & seconds you could also use NSCalendar and in particular the components:fromDate:toDate:options: method. Take a look at this question for some more discussion on that:
Number of days between two NSDates

NSTimer Count Up

I am trying to create an NSTimer that counts up from 00:00:00 to whenever the user presses stop. I have created an NSDateInterval of 1 second, but I can't get it to count up from 00:00:00, only the current date or a date that I set.
What is the best way to do this? And is NSTimer the best way to do this or should I just get the difference between the time the user presses the first button ([NSDate date] to set it) and the time at that second (a selector fired off by NSTimer)? I want to make sure there is as little accuracy fault as possible, and I am aware of NSTimer's problems with accuracy sometimes.
Save the time the timer starts with [NSDate date], and then record the time it stops as well. The display time will be the difference, displayed as you listed. The NSTimer will be used just to trigger an update, and you can set the interval fairly small, say 1/10th of a second. Something like:
[NSTimer scheduledTimerWithTimeInterval: 0.1f target: self
selector: #selector(updateTimeDisplay) userInfo: nil repeats: YES];