使用NSTimer的秒表不正确地包括暂停时间

这是我的iPhone秒表的代码。 它按预期工作,当点击button时停止并继续。

然而,当我点击“停止”时,计时器不会停止在后台运行,当我点击“开始”恢复时,它会更新时间并跳到当前的位置,而不是从停止的时间恢复。

我怎样才能停止NSTimer ? 是什么导致这种情况发生?

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

您对当前显示的计算总是使用定时器的原始开始时间,因此暂停后的显示包括定时器暂停的时间间隔。

最简单的做法是在定时器暂停时存储另一个NSTimeInterval ,例如secondsAlreadyRun ,并将其添加到恢复时计算的时间间隔中。 每当定时器开始计数时,您都需要更新定时器的startDate 。 在reset: ,你也可以清除那个secondsAlreadyRun间隔。

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

不要忘记在适当的地方释放startDate ! 另外请记住,logging的NSTimer接口是用于接受一个参数的方法,它将是定时器本身。 它似乎没有这个工作,但为什么试探命运?

最后,由于您非常使用NSDateFormatter因此您可能需要考虑将其设置为ivar或将其放置在showActivity:中的static存储中,如下所示:

 static NSDateFormatter * dateFormatter = nil; if( !dateFormatter ){ dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"mm:ss.SS"]; [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]]; } 

所以,当用户按下停止,然后重新开始,你不会重新设置开始时间。 但是当您更新标签时,您将基于从原始开始时间到当前时间的总时间。

所以,如果你运行定时器10秒,停止,等待10秒,然后再次启动,定时器将显示00:20.00,并从那里开始再次计数。

你想要做的是每次用户开始时钟时重新设置开始时间,但是也要添加所有以前的运行时间。 或类似的东西。

顺便说一句,你现在每次重置它都会泄漏开始时间。 小错误。

编辑:看起来像@乔什·卡斯韦尔在想同样的事情,但他更快地键入一个很多。 🙂

你使用ARC还是不?

如果你正在使用ARC,看起来你不使用_strong引用。 如果您不使用ARC,则不会保留对定时器的引用。

我从移动发布这个可能会丢失的东西。

编辑:只是注意到你在其他地方使用释放,所以我会假设没有ARC。 您需要保留定时器,以便能够稍后访问并使其无效。

您可以使用NSTimeInterval而不是定时器。 我有一个function代码来暂停和停止计时器。

 @interface PerformBenchmarksViewController () { int currMinute; int currSecond; int currHour; int mins; NSDate *startDate; NSTimeInterval secondsAlreadyRun; } @end - (void)viewDidLoad { [super viewDidLoad]; running = false; } - (IBAction)StartTimer:(id)sender { if(running == false) { //start timer running = true; startDate = [[NSDate alloc] init]; startTime = [NSDate timeIntervalSinceReferenceDate]; [sender setTitle:@"Pause" forState:UIControlStateNormal]; [self updateTime]; } else { //pause timer secondsAlreadyRun += [[NSDate date] timeIntervalSinceDate:startDate]; startDate = [[NSDate alloc] init]; [sender setTitle:@"Start" forState:UIControlStateNormal]; running = false; } } - (void)updateTime { if(running == false) return; //calculate elapsed time NSTimeInterval currentTime = [NSDate timeIntervalSinceReferenceDate]; NSTimeInterval elapsed = secondsAlreadyRun + currentTime - startTime; // extract out the minutes, seconds, and hours of seconds from elapsed time: int hours = (int)(mins / 60.0); elapsed -= hours * 60; mins = (int)(elapsed / 60.0); elapsed -= mins * 60; int secs = (int) (elapsed); //update our lable using the format of 00:00:00 timerLabel.text = [NSString stringWithFormat:@"%02u:%02u:%02u", hours, mins, secs]; //call uptadeTime again after 1 second [self performSelector:@selector(updateTime) withObject:self afterDelay:1]; } 

希望这会有所帮助。 谢谢

我在Swift中创build了一个定时器类,用于定时器程序,其中计数器在设定的时间内每秒更新一次。 回答说明了Swift解决scheme和NSTimer函数。

定时器可以停止并重新启动; 它将从停止的地方恢复。 事件可以被代表拦截,用于开始,停止,重置,结束和第二事件。 只需检查代码。

 import Foundation protocol TimerDelegate { func didStart() func didStop() func didReset() func didEnd() func updateSecond(timeToGo: NSTimeInterval) } // Inherit from NSObject to workaround Selector bug class Timer : NSObject { var delegate: TimerDelegate? var defaultDuration: NSTimeInterval? var isRunning: Bool { get { return self.timer != nil && timer!.valid } } private var secondsToGo: NSTimeInterval = 0 private var timer: NSTimer? init(defaultDuration: NSTimeInterval, delegate: TimerDelegate? = nil) { self.defaultDuration = defaultDuration self.delegate = delegate super.init() } func start() { self.timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "updateTimer", userInfo: nil, repeats: true) self.timer!.tolerance = 0.05 if delegate != nil { delegate!.didStart() } } func stop () { self.timer?.invalidate() self.timer = nil if delegate != nil { delegate!.didStop() } } func reset() { self.secondsToGo = self.defaultDuration! if delegate != nil { delegate!.didReset() } } func updateTimer() { --self.secondsToGo if delegate != nil { delegate!.updateSecond(self.secondsToGo) } if self.secondsToGo == 0 { self.stop() if delegate != nil { delegate!.didEnd() } } } }