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";
}
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]];
}