Stopwatch using NSTimer incorrectly includes paused time in display
cocoa-touch, ios, nstimer, objective-c, time
Solution
Your calculation of the current display always uses the original start time of the timer, so the display after pausing includes the interval that the timer was paused.
The easiest thing to do would be to store another `NSTimeInterval`, say `secondsAlreadyRun`, when the timer is paused, and add that to the time interval you calculate when you resume. You'll want to update the timer's `startDate` every time the timer starts counting. In `reset:`, you would also clear out that `secondsAlreadyRun` interval.
-(void)showActivity:(NSTimer *)tim {
NSDate *currentDate = [NSDate date];
NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate];
// Add the saved interval
timeInterval += secondsAlreadyRun;
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"mm:ss.SS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
NSString *timeString=[dateFormatter stringFromDate:timerDate];
stopWatchLabel.text = timeString;
[dateFormatter release];
}
- (IBAction)onStartPressed:(id)sender {
stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1/10
target:self
selector:@selector(showActivity:)
userInfo:nil
repeats:YES];
// Save the new start date every time
startDate = [[NSDate alloc] init]; // equivalent to [[NSDate date] retain];
[stopWatchTimer fire];
}
- (IBAction)onStopPressed:(id)sender {
// _Increment_ secondsAlreadyRun to allow for multiple pauses and restarts
secondsAlreadyRun += [[NSDate date] timeIntervalSinceDate:startDate];
[stopWatchTimer invalidate];
stopWatchTimer = nil;
[startDate release];
[self showActivity];
}
- (IBAction)reset:(id)sender; {
secondsAlreadyRun = 0;
stopWatchLabel.text = @"00:00.00";
}
Don't forget to release that `startDate` somewhere appropriate! Also keep in mind that the documented `NSTimer` interface is for the method you give it to accept one argument, which will be the timer itself. It seems to work without that, but why tempt fate?
Finally, since you're using that `NSDateFormatter` so much, you might want to consider making it an ivar or put it in `static` storage in `showActivity:`, like so:
static NSDateFormatter * dateFormatter = nil;
if( !dateFormatter ){
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"mm:ss.SS"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
}
Problem
This is my code for an iPhone stopwatch. It works as expected and stops and resumes when the buttons are clicked. When I hit "Stop", however, the timer won't stop running in the background, and when I hit "Start" to resume it, it will update the time and skip to where it is currently instead of resuming from the stopped time. How can I stop the `NSTimer`? What is causing this to occur? ``` @implementation FirstViewController; @synthesize stopWatchLabel; NSDate *startDate; NSTimer *stopWatchTimer; int touchCount; -(void)showActivity { NSDate *currentDate = [NSDate date]; NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:startDate]; NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval]; NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"mm:ss.SS"]; [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]]; NSString *timeString=[dateFormatter stringFromDate:timerDate]; stopWatchLabel.text = timeString; [dateFormatter release]; } - (IBAction)onStartPressed:(id)sender { stopWatchTimer = [NSTimer scheduledTimerWithTimeInterval:1/10 target:self selector:@selector(showActivity) userInfo:nil repeats:YES]; touchCount += 1; if (touchCount > 1) { [stopWatchTimer fire]; } else { startDate = [[NSDate date]retain]; [stopWatchTimer fire]; } } - (IBAction)onStopPressed:(id)sender { [stopWatchTimer invalidate]; stopWatchTimer = nil; [self showActivity]; } - (IBAction)reset:(id)sender; { touchCount = 0; stopWatchLabel.text = @"00:00.00"; } ```