Подтвердить что ты не робот

Как я могу программно приостановить NSTimer?

Я использую NSTimer для выполнения рендеринга в iPhone на базе OpenGL. У меня есть модальное диалоговое окно, которое появляется и запрашивает ввод пользователя. Пока пользователь предоставляет ввод, я хотел бы "приостановить", например, что-то вроде этого:

[myNSTimer pause];

Я использую этот синтаксис, потому что я делал такие вещи, как:

[myNSTimer invalidate];

когда я хочу, чтобы он остановился.

Как я могу программно приостановить NSTimer?

4b9b3361

Ответ 1

Отсюда:

http://discussions.apple.com/thread.jspa?threadID=1811475&tstart=75

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

Я не думаю, что есть способ сделать паузу и перезапустить таймер. Я столкнулся с подобной ситуацией. "

Ответ 2

Просто подумал о том, чтобы обновить незначительные исправления до kapesoftware answer:

NSDate *pauseStart, *previousFireDate;

-(void) pauseTimer:(NSTimer *)timer { 

    pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];

    previousFireDate = [[timer fireDate] retain];

    [timer setFireDate:[NSDate distantFuture]];
}

-(void) resumeTimer:(NSTimer *)timer {

    float pauseTime = -1*[pauseStart timeIntervalSinceNow];

    [timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];

    [pauseStart release];

    [previousFireDate release];
}

Ответ 3

То, как я это сделал, это сохранить дату запуска NSTimer в переменной, а затем установить дату увольнения в INFINITY.

Когда я приостанавливаю:

pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFiringDate = [timer firingDate];
[timer setFiringDate:INFINITY]

Когда я приостанавливаю, я добавляю количество времени, в течение которого таймер был приостановлен до предыдущей даты увольнения:

float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];

Это сделало его намного легче, чем беспокоиться о недействительности и повторной инициализации таймера. У меня было много таймеров, поэтому я просто поместил их в массив и сделал это со всеми из них. Я подклассифицировал NSTimer, чтобы сохранить предыдущийFiringDate, связанный с этим таймером.

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

Ответ 4

В моем случае у меня была переменная "секунды", а другая "timerIsEnabled". Когда я хотел приостановить таймер, просто сделал timerIsEnabled ложным. Поскольку переменная секунд была только увеличена, если timerIsEnabled был истинным, я исправил свою проблему. Надеюсь, это поможет.

Ответ 5

Самый простой способ, которым я смог это сделать, был следующим:

-(void) timerToggle{
    if (theTimer == nil) {
        float theInterval = 1.0/30.0;
        theTimer = [NSTimer scheduledTimerWithTimeInterval:theInterval target:self selector:@selector(animateBall:) userInfo:nil repeats:YES];
    } else {
        [theTimer invalidate];
        theTimer = nil;
    }
}

Ответ 6

Вот категория на NSTimer, которая позволяет использовать функции паузы и возобновления.

Заголовок:

#import <Foundation/Foundation.h>

@interface NSTimer (CVPausable)

- (void)pauseOrResume;
- (BOOL)isPaused;

@end

Реализация:

#import "NSTimer+CVPausable.h"
#import <objc/runtime.h>

@interface NSTimer (CVPausablePrivate)

@property (nonatomic) NSNumber *timeDeltaNumber;

@end

@implementation NSTimer (CVPausablePrivate)

static void *AssociationKey;

- (NSNumber *)timeDeltaNumber
{
    return objc_getAssociatedObject(self, AssociationKey);
}

- (void)setTimeDeltaNumber:(NSNumber *)timeDeltaNumber
{
    objc_setAssociatedObject(self, AssociationKey, timeDeltaNumber, OBJC_ASSOCIATION_RETAIN);
}

@end


@implementation NSTimer (CVPausable)

- (void)pauseOrResume
{
    if ([self isPaused]) {
        self.fireDate = [[NSDate date] dateByAddingTimeInterval:[self.timeDeltaNumber doubleValue]];
        self.timeDeltaNumber = nil;
    }
    else {
        NSTimeInterval interval = [[self fireDate] timeIntervalSinceNow];
        self.timeDeltaNumber = @(interval);
        self.fireDate = [NSDate distantFuture];
    }
}

- (BOOL)isPaused
{
    return (self.timeDeltaNumber != nil);
}

@end

Ответ 7

Старый вопрос, но я просто написал легкий класс полезности, чтобы выполнить именно то, что ищет OP.

https://github.com/codeslaw/CSPausibleTimer

Поддерживает повторяющиеся таймеры. Надеюсь, это пригодится!

Ответ 8

Основываясь на ответах @Thiru и @kapesoftware, я сделал категорию Objective-C на NSTimer, используя Associated Objects для приостановки и возобновления таймера.

#import <objc/runtime.h>

@interface NSTimer (PausableTimer)

@property (nonatomic, retain) NSDate *pausedDate;

@property (nonatomic, retain) NSDate *nextFireDate;

-(void)pause;

-(void)resume;

@end

...

@implementation NSTimer (PausableTimer)

static char * kPausedDate = "pausedDate";
static char * kNextFireDate = "nextFireDate";

@dynamic pausedDate;
@dynamic nextFireDate;

-(void)pause {

  self.pausedDate = [NSDate date];

  self.nextFireDate = [self fireDate];

  [self setFireDate:[NSDate distantFuture]];
}

-(void)resume
{
  float pauseTime = -1*[self.pausedDate timeIntervalSinceNow];

  [self setFireDate:[self.nextFireDate initWithTimeInterval:pauseTime sinceDate:self.nextFireDate]];
}

- (void)setPausedDate:(NSDate *)pausedDate
{
  objc_setAssociatedObject(self, kPausedDate, pausedDate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSDate *)pausedDate
{
  return objc_getAssociatedObject(self, kPausedDate);
}

- (void)setNextFireDate:(NSDate *)nextFireDate
{
  objc_setAssociatedObject(self, kNextFireDate, nextFireDate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSDate *)nextFireDate
{
  return objc_getAssociatedObject(self, kNextFireDate);
}

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

Ответ 10

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

При создании таймера инициализируйте время запуска, например:

self.startDate=[NSDate date];
self.timeElapsedInterval=[[NSDate date] timeIntervalSinceDate:self.startDate];//This ``will be 0 //second at the start of the timer.``     

myTimer= [NSTimer scheduledTimerWithTimeInterval:1.0 target:self `selector:@selector(updateTimer) userInfo:nil repeats:YES];

` Теперь в методе таймера обновления:

  NSTimeInterval unitTime=1;
-(void) updateTimer
 {
 if(self.timerPaused)
       {
           //Do nothing as timer is paused
       }
 else{
     self.timerElapsedInterval=timerElapsedInterval+unitInterval;
    //Then do your thing update the visual timer texfield or something.
    }

 }

Ответ 11

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

Следующий код работает с ответом на нажатие кнопки паузы.

-(IBAction)Pause:(id)sender
{
    static BOOL *PauseToggle;
    if(!PauseToggle)
    {
        [timer invalidate];
        timer = nil;
        PauseToggle = (BOOL *) YES;
    }
    else
    {
        timer = [NSTimer scheduledTimerWithTimeInterval:0.04 target:self selector:@selector(HeliMove) userInfo:nil repeats:YES];
        PauseToggle = (BOOL *) NO;
    }
}

Ответ 12

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

Таймер запускается при загрузке (initiateFeatureTimer) и приостанавливает взаимодействие с пользователем (начнет метод перетаскивания), позволяя пользователю просматривать контент до тех пор, пока она любит. Когда пользователь поднимает свой палец (закончится перетаскивание), функция Boolean имеет значение reset, но также вы можете добавить немного больше задержки.

Таким образом, скроллер не мгновенно отключается от подъема пальца, он более реактивен для пользователя.

//set vars, fire first method on load.
featureDelay = 8; //int
featureInt = featureDelay-1; //int
featurePaused = false; //boolean

-(void)initiateFeatureTimer {
    featureTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(moveFeatureScroller) userInfo:nil repeats:true];
    [featureTimer fire];
}
 -(void)moveFeatureScroller {

    if (!featurePaused){ //check boolean
        featureInt++;

         if (featureInt == featureDelay){
             featureInt = 0; //reset the feature int

            /*//scroll logic
            int nextPage = (featurePage + 1) * w;
            if (featurePage == features.count-1){ nextPage = 0; }
            [featureScroller setContentOffset:CGPointMake(nextPage, 0)]; */          
         }
      }
}

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    featurePaused = true; //pause the timer
}
-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    featurePaused = false; //restart the timer
    featureInt = -3; //if you want to add an additional delay
}

Ответ 14

Если ваш временной интервал срабатывания толерантен, вы можете использовать CADisplayLink вместо NSTimer. Потому что CADisplayLink поддерживает -pause.

displayLink = [[CADisplayLink displayLinkWithTarget:self selector:@selector(fireMethod:)];
displayLink.frameInterval = approximateVaue;//CADisplayLink frame rate is 60 fps.
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

//pause
[displayLink pause];

Ответ 15

Мне также нужен pausable NSTimer. После прочтения этой темы и ваших ответов и осознания того, что мне нужно только установить таймер FireDate в далекое будущее, а затем вернуться к Теперь я сделал категорию для NSTimer с помощью методов Pause и Resume. Поэтому я могу просто приостановить таймер, вызвав [myTimer pause]; и возобновить, вызвав [myTimer resume]; методы. Вот он, надеюсь, что это поможет кому-то.

Интерфейс:

#import <Foundation/Foundation.h>

@interface NSTimer (Pausable)

-(void)pause;
-(void)resume;

@end

Реализация:

#import "NSTimer+Pausable.h"

@implementation NSTimer (Pausable)

-(void)pause
{
    [self setFireDate:[NSDate dateWithTimeIntervalSinceNow:[NSDate distantFuture]]]; //set fireDate to far future
}

-(void)resume
{
    [self setFireDate:[NSDate date]];
}

@end