使用NSTimer的秒表错误地包括显示中的暂停时间

时间:2011-11-11 23:36:51

标签: objective-c ios cocoa-touch time nstimer

这是我的iPhone秒表代码。它按预期工作,并在单击按钮时停止并恢复。

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

如何停止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";
}

5 个答案:

答案 0 :(得分:10)

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

最简单的方法是在计时器暂停时存储另一个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!还要记住,记录的NSTimer接口适用于您接受一个参数的方法,该参数将是计时器本身。它似乎没有那个,但为什么诱惑命运?

最后,由于您使用NSDateFormatter这么多,您可能需要考虑将其设为ivar或将其放在static的{​​{1}}存储中,如下所示:

showActivity:

答案 1 :(得分:2)

因此,当用户按下停止,然后再次启动时,您不会重置开始时间。但是当您更新标签时,您将基于从原始开始时间到当前时间的总耗用时间。

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

您要做的是每次用户启动时钟时重置开始时间,然后再添加所有先前运行的已用时间。或类似的东西。

顺便说一下,每次重置时都会泄漏开始时间。小错误。

编辑:看起来像@Josh Caswell正在考虑同样的事情,但他输入的速度更快。 :)

答案 2 :(得分:0)

您是否使用ARC?

如果您使用ARC,看起来您不使用_strong引用。如果您不使用ARC,则表示您不会保留对计时器的引用。

我是从手机发布的,所以可能会遗漏一些东西。

编辑:刚刚注意到你在其他地方使用发布,所以我假设没有ARC。您需要在设置计时器之后保留计时器,以便以后能够访问它并使其无效。

答案 3 :(得分:0)

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

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

希望这会有所帮助。感谢

答案 4 :(得分:0)

我在Swift中为计时器程序创建的计时器类,其中计数器从设定时间每秒更新一次。回答说明Swift解决方案和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() }
    }
  }

}