Como posso pausar programaticamente um NSTimer?

Eu estou usando um NSTimer para fazer alguma renderização em um aplicativo para iPhone baseado em OpenGL. Eu tenho uma checkbox de modal dialog que aparece e solicita a input do usuário. Enquanto o usuário está fornecendo informações, eu gostaria de “pausar”, algo assim:

[myNSTimer pause]; 

Estou usando essa syntax porque tenho feito coisas como:

 [myNSTimer invalidate]; 

quando eu quero parar.

Como posso pausar programaticamente o NSTimer?

Daqui:

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

“Você pode armazenar a quantidade de tempo que passou desde que o cronômetro começou … Quando o cronômetro começa a armazenar a data em uma variável NSDate. Então quando o usuário muda … use o método timeIntervalSinceNow na class NSDate para armazenar quanto O tempo passou … note que isso vai dar um valor negativo para timeIntervalSinceNow. Quando o usuário retorna use esse valor para definir um timer apropriado.

Não acho que haja uma maneira de pausar e reiniciar um cronômetro. Eu enfrentei uma situação semelhante. ”

Apenas pensei em atualizar pequenas correções para a resposta do kapesoftware :

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

A maneira como consegui isso foi armazenar a data de disparo do NSTimer em uma variável e depois definir a data de disparo para INFINITY.

Quando eu paro:

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

Quando eu interrompo, adiciono a quantidade de tempo que o cronômetro foi pausado para a data de disparo anterior:

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

Isso tornou muito mais fácil do que se preocupar em invalidar e reinicializar o cronômetro. Eu tinha muitos timeres, então coloquei todos em uma matriz e fiz isso para todos eles. Eu subclass o NSTimer para manter o previousFiringDate associado a esse timer.

Isso também era melhor do que usar um BOOL se estava ou não pausado, então as ações não aconteceriam se o BOOL estivesse definido. Isso ocorre porque se algo estava prestes a acontecer antes de você fazer uma pausa, isso não acontecerá depois que você o interromper, porque ele terá sido ignorado.

No meu caso, eu tinha uma variável ‘seconds’ e outra ‘timerIsEnabled’. Quando eu queria pausar o timer, apenas fiz o timerIsEnabled como falso. Como a variável segundos só foi incrementada se timerIsEnabled fosse true, eu consertei meu problema. Espero que isto ajude.

A maneira mais fácil que consegui fazer foi assim:

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

Aqui está uma categoria no NSTimer que permite pausar e retomar a funcionalidade.

Cabeçalho:

 #import  @interface NSTimer (CVPausable) - (void)pauseOrResume; - (BOOL)isPaused; @end 

Implementação:

 #import "NSTimer+CVPausable.h" #import  @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 

Pergunta antiga, mas acabei de escrever uma class de utilitário leve para realizar exatamente o que o OP está procurando.

https://github.com/codeslaw/CSPausibleTimer

Suporta a repetição de timeres também. Espero que seja útil!

Com base nas respostas do @Thiru e @kapesoftware, criei uma categoria Objective-C no NSTimer usando Associated Objects para pausar e retomar o timer.

 #import  @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); } 

Ele mantém tudo organizado e significa que você não precisa criar variables ​​ou propriedades de instâncias apenas para pausar e retomar seus timers.

Sinta-se à vontade para reutilizar minha categoria no NSTimer :

https://github.com/keeshux/ios-components/tree/master/Components/Categories/NSTimer%2BPause

Você não pode pausar o NSTimer como mencionado acima. Então o tempo a ser capturado não deve depender do Timed Firedate, eu sugiro. Aqui está a minha solução mais simples:

Ao criar o cronômetro, inicialize a hora da unidade inicial como:

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

`Agora no método do timer de atualização:

  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. } } 

Bem, eu acho isso como o melhor método para implementar a pausa de uma coisa timer. Use uma variável estática para alternar a pausa e retomar. Em pausa, invalide o timer e defina-o como nil e, na seção de reinício, redefina o timer usando o código original pelo qual foi iniciado.

O código a seguir funciona na resposta a um clique no botão de pausa.

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

O código abaixo é para uso com uma visão de rolagem, que traz conteúdo em intervalos regulares, e volta para a frente na conclusão, mas a mesma lógica pode ser usada para qualquer botão de pausa / reboot.

O timer triggers no carregamento (initiateFeatureTimer) e faz uma pausa para interação do usuário (começará a arrastar o método), permitindo que o usuário examine o conteúdo pelo tempo que desejar. Quando o usuário levanta o dedo (termina arrastando), o valor booleano featurePaused é redefinido, mas você também tem a opção de adicionar um pouco mais de atraso.

Desta forma, o scroller não se move instantaneamente com o elevador do dedo, é mais reativo para o usuário.

 //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 } 

Aqui está um timer simples de pausa / reprodução.

https://github.com/AnthonyUccello/Swift-Pausable-Timer/blob/master/Timer.swift

Se o seu intervalo de tempo de incêndio for tolerante, você poderá usar CADisplayLink vez de NSTimer . Porque o CADisplayLink suporta a CADisplayLink .

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

Eu também estava precisando de NSTimer pausável. Depois de ler este tópico e suas respostas e perceber que a única coisa que eu preciso é definir fireDate do timer para o futuro distante e, em seguida, voltar para agora eu fiz categoria para NSTimer com os methods Pause e Resume. Então, posso simplesmente pausar o timer chamando [myTimer pause]; e retomar chamando [myTimer resume]; methods. Aqui está, espero que ajude alguém.

Interface:

 #import  @interface NSTimer (Pausable) -(void)pause; -(void)resume; @end 

Implementação:

 #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