Секундомер, использующий NSTimer, неправильно отображает время паузы на дисплее - PullRequest
3 голосов
/ 12 ноября 2011

Это мой код для секундомера 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 ]

10 голосов
/ 12 ноября 2011

При расчете текущего дисплея всегда используется исходное время запуска таймера, поэтому дисплей после паузы включает интервал, в течение которого таймер был приостановлен.

Самое простое, что можно сделать, это сохранить другое 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, вы можете подумать о том, чтобы сделать его иваром или поместить его в хранилище static вshowActivity:, вот так:

static NSDateFormatter * dateFormatter = nil;
if( !dateFormatter ){
    dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"mm:ss.SS"];
    [dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
}
2 голосов
/ 12 ноября 2011

Итак, когда пользователь нажимает «Стоп», а затем запускается снова, вы не сбрасываете время запуска.Но когда вы обновляете метку, вы основываете это на общем истекшем времени от исходного времени начала до текущего времени.

Итак, если вы запустите таймер на 10 секунд, остановитесь, подождите 10 секунд, а затем начнете снова, таймер покажет 00: 20.00 и снова начнет отсчет.

Что вы хотитеДля этого нужно сбросить время запуска каждый раз, когда пользователь запускает часы, но затем добавить также прошедшее время всех предыдущих запусков.Или что-то подобное.

Кстати, вы теряете время запуска каждый раз, когда сбрасываете его сейчас.Небольшая ошибка.

РЕДАКТИРОВАТЬ: похоже, @Josh Caswell думал то же самое, но он печатает много быстрее.:)

0 голосов
/ 05 ноября 2014

Класс таймера, который я создал в 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() }
    }
  }

}
0 голосов
/ 19 марта 2013

Вы можете использовать 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];
}

Надеюсь, это поможет.Спасибо

0 голосов
/ 12 ноября 2011

Вы используете ARC или нет?

Если вы используете ARC, похоже, вы не используете _strong ссылку. Если вы не используете ARC, значит, у вас нет ссылки на таймер.

Я выкладываю это с мобильного телефона, поэтому, возможно, что-то упустил.

РЕДАКТИРОВАТЬ: только что заметил, что вы использовали релиз в другом месте, поэтому я не буду считать ARC. Вы должны сохранить таймер после установки, чтобы иметь возможность получить к нему доступ позже и сделать его недействительным.

...