Приостановка NSTimer

#iphone #nstimer #nsthread

#iPhone #nstimer #nsthread

Вопрос:

У меня есть UIView с UITableView и UIImageView в нем. UITableView Занимает верхнюю половину UIView . UIImageView Занимает нижнюю половину UIView .

Я использую NSTimer для обновления изображения, которое отображается в UIImageView . В принципе, каждые 3 секунды UIImageView загружается новый UIImage .

Проблема в том, что если я выбираю строку из UITableView , NSTimer продолжает выполняться. Как я могу остановить NSTimer включение didSelectRowFromIndexPath , а затем запустить его снова, когда просмотр вернется на экран?

 -(void)viewDidLoad {
NSThread* timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimerThread) object:nil]; //Create a new thread
    [timerThread start]; //start the thread

} // end viewDidLoad

//
-(void) startTimerThread {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
    [[NSTimer scheduledTimerWithTimeInterval: 3.0
                                      target: self
                                    selector: @selector(loadNextPhoto)
                                    userInfo: nil
                                     repeats: YES] retain];

    [runLoop run];
    [pool release];
}
  

Ответ №1:

Сохраните ссылку на созданный вами таймер, чтобы при необходимости его можно было остановить. В следующий раз, когда вам это понадобится, вы можете просто создать новый таймер. Похоже, что лучшие места для этого — viewDidAppear: и viewWillDisappear: . Я не уверен, почему вы запускаете таймер в новом потоке. Возможно, у вас есть веские причины для этого, но мне никогда не нужно было этого делать.

 //This code assumes you have created a retained property for loadNextPhotoTimer

    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self startTimer];
    }

    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        [self stopTimer];
    }

    - (void)startTimer {
        NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:3.0] interval:3.0 target:self selector:@selector(loadNextPhoto) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        self.loadNextPhotoTimer = timer;
        [timer release];
    }

    - (void)stopTimer {
        [self.loadNextPhotoTimer invalidate];
        self.loadNextPhotoTimer = nil;
    }
  

Комментарии:

1. у меня это решение не работает… Мой таймер создается с помощью «scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:» … должен ли я явно указывать цикл выполнения, к которому необходимо добавить таймер?

2. Хорошо, я изменил свою реализацию startTimer, чтобы она была точно такой же, как указано выше, и она работает….

Ответ №2:

Вы можете использовать invalidate метод NSTimer для уничтожения таймера.

Затем вам придется воссоздать его, как вы делали в своем startTimerThread методе, когда вы вернетесь к просмотру.

Ответ №3:

Чтобы получить доступ к потоку извне startTimerThread метода, вы должны добавить свойство в свой класс. Я бы предложил использовать свойство поверх ivar, потому что вы можете автоматически синтезировать атомарные средства доступа. То, что вы делаете с таймером, зависит от того, хотите ли вы приостановить или остановить его.

Остановка таймера означает, что при следующем запуске он запустится с оставшимися 3 секундами. Это означает, что если таймер сработает через 1 секунду при остановке, то после повторного запуска все равно потребуется 3 секунды, чтобы сработать. Однако остановить очень просто. stopTimerThread Будет определяться следующим образом:

 - (void)stopTimerThread {
    NSTimer *theTimer = self.timer;
    [theTimer invalidate];
    self.timer = nil;
}
  

Поскольку цикл выполнения в вашем другом потоке теперь не имеет источников, он автоматически завершится, и поток завершится, так что вы можете использовать его startTimerThread , когда захотите запустить его снова.

Приостановка работы таймера означает, что если при приостановке он сработает через 1 секунду, то через 1 секунду после перезапуска он сработает. Для этого вам также понадобится свойство, содержащее время, оставшееся до срабатывания таймера, которое будет установлено в stopTimerThread и использовано в startTimerThread . Реализация stopTimerThread заключается в следующем:

 - (void)stopTimerThread {
    NSTimer *theTimer = self.timer;
    NSDate *fireDate = [theTimer fireDate];
    [theTimer invalidate];
    self.timerTimeRemaining = - [fireDate timeIntervalSinceNow];
    self.timer = nil;
}
  

Обратите внимание, что оставшееся время установлено равным отрицательному значению временного интервала, поскольку дата срабатывания будет раньше текущего момента. Вам также придется внести изменения startTimerThread , чтобы учитывать оставшееся время при настройке таймера.

 - (void)startTimerThread {
    // set up autorelease pool, get run loop
    NSTimeInterval timeLeft = self.timerTimeRemaining;
    if(timeLeft <= 0.0) timeLeft = 3.0;
    NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:timeLeft];
    NSTimer *theTimer = [[NSTimer alloc] initWithFireDate:fireDate interval:3.0 target:self selector:@selector(loadNextPhoto) userInfo:nil repeats:YES];
    [runLoop addTimer:theTimer forMode:NSDefaultRunLoopMode];
    self.timer = theTimer;
    [theTimer release];
    [runLoop run];
    // clean up
}
  

Ответ №4:

Создайте флаг (BOOL) для управления обновлением изображения.

Очистите его, если хотите, чтобы процедура, обновляющая изображение, игнорировала тот факт, что она была вызвана запуском события таймера. Если вы хотите возобновить обновления изображения, просто установите флажок. Может быть, что-то вроде этого:

 -(void)ImageUpdater:(NSTimer *)theTimer
{
  if(image_updates_enabled)
  {
     // Code to update image
  }
}
  

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

Еще одна вещь, которую вы можете сделать, это настроить таймер так, чтобы он не повторялся. С учетом этого решение о повторном включении таймера может быть принято где-то еще в вашем коде, а не автоматически. Вы могли бы даже сделать это в функции обратного вызова и по-прежнему использовать флаг «image_updates_enabled», если хотите.