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