2008-12-07 7 views
41

J'utilise un NSTimer pour faire du rendu dans une application iPhone basée sur OpenGL. J'ai une boîte de dialogue modale qui apparaît et demande une entrée de l'utilisateur. Bien que l'utilisateur fournit l'entrée, je voudrais « pause », à savoir quelque chose comme ceci:Comment puis-je mettre en pause par programme un NSTimer?

[myNSTimer pause]; 

J'utilise cette syntaxe parce que je fais des choses comme:

[myNSTimer invalidate]; 

quand je Je veux qu'il arrête.

Comment puis-je mettre en pause par programme le NSTimer?

+9

@fishcharlie le lien est rompu – Till

Répondre

20

D'ici:

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

« Vous pouvez stocker la quantité de temps écoulé depuis la minuterie a commencé ... Lorsque le chronomètre démarre stocker la date dans une variable NSDate Ensuite, lorsque l'utilisateur. switch ... utilise la méthode timeIntervalSinceNow de la classe NSDate pour stocker combien de temps s'est écoulé ... notez que cela donnera une valeur négative à timeIntervalSinceNow.Lorsque l'utilisateur retourne cette valeur, utilisez cette valeur pour régler un timer approprié

Je ne pense pas qu'il y ait un moyen de faire une pause et de redémarrer une minuterie. uation. "

+3

Merci pour la réponse. Peut-être peut-il être résolu en se débarrassant de la première minuterie ... puis en créant une toute nouvelle minuterie après que l'utilisateur ait fourni l'entrée. – MrDatabase

+1

Pour mettre votre minuteur en pause, envoyez le message 'invalider' à votre minuterie et réglez-le à zéro par la suite. Pour reprendre la minuterie, réglez-la sur [NSTimer schedul .....]. Fondamentalement, se débarrasser de votre ancien et ensuite en créer un nouveau avec les mêmes propriétés. Ce que j'ai trouvé le plus simple :) –

+1

Ce n'est pas la même chose. Si vous mettez en pause une minuterie, il reste du temps avant l'événement suivant. Si vous le supprimez et en commencez un nouveau, il aura toujours le temps d'origine avant un événement, ce qui est faux. –

8

Dans mon cas, j'avais une variable" secondes ", et une autre" timerIsEnabled ".Quand je voulais mettre le timer en pause, je faisais juste le timerIsEnabled comme faux.La variable secondes n'étant incrémentée que si timerIsEnabled était vrai, Je fixe mon problème Espérons que cela aide

10

la façon dont j'accompli c'était en stockant la date de mise à feu de NSTimer dans une variable puis fixer la date de mise à feu à INFINITY

Quand je pause...

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

Quand j'appuie, j'ajoute l'amou nt du temps que la minuterie a été interrompue à la date de mise à feu précédente:

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

Cela a beaucoup plus facile que de se soucier et invalidant réinitialisant la minuterie. J'ai eu beaucoup de minuteurs alors je les ai tous mis dans un tableau et fait cela à tous. J'ai sous-classé le NSTimer afin de garder le previousFiringDate associé à ce timer.

Cela valait aussi mieux que d'utiliser un BOOL, qu'il soit ou non en pause, de sorte que les actions n'auraient pas lieu si le BOOL était activé. C'est parce que si quelque chose était sur le point de se produire avant que vous mettiez en pause, cela ne se produira pas après que vous l'ayez interrompu parce qu'il aura été simplement ignoré.

+1

Cela peut fonctionner, mais selon le [document de référence] (http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer.html): 'Sous-classe Notes Vous ne devriez pas essayer de sous-classer NSTimer. » – Jon

7

La meilleure façon que je réussi à le faire était comme ça:

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

Que signifie le 1.0/30.0? – zakdances

+0

@yourfriendzak: C'est le taux/intervalle de tir auquel le sélecteur de la minuterie est appelé. Dans ce cas, 1/30 est toutes les 0,03s. – Luke

35

Je pensais juste que de mettre à jour des corrections mineures à la réponse de 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]; 
} 
+0

cela fonctionne très bien mais quand je le mets en pause .. leur pause temps et quand je le reprends ... il commence avec l'heure actuelle – Christien

+3

Au lieu de '[NSDate dateWithTimeIntervalSinceNow: 0]' vous pouvez simplement faire '[NSDate date]' . – DarkDust

0

Vous ne pouvez pas mettre en pause NSTimer comme mentionné ci-dessus. Donc, le temps d'être capturé ne devrait pas dépendre de Timer firedate, je suggère.Voici ma solution la plus simple:

Lors de la création de la minuterie initialiser le temps de l'unité de départ comme:

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

` maintenant dans la méthode de la minuterie de mise à jour:

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

} 
2

question ancienne, mais je a écrit une classe utilitaire légère pour accomplir exactement ce que OP recherche.

https://github.com/codeslaw/CSPausibleTimer

soutient la répétition des minuteries aussi. J'espère que c'est utile!

+0

Cela semble génial. Je vérifie ça maintenant. Merci! –

+0

Votre code indique "Copyright 2013" et "Tous droits réservés". cela vous dérange si je l'utilise dans un projet potentiellement rentable? –

+0

@thunder rabbit - oui, faites s'il vous plaît. Je vais être sûr de changer le texte de la licence quand j'ai une chance. Vous êtes libre de l'utiliser comme vous le souhaitez – Chris

5

Voici une catégorie sur NSTimer qui permet de mettre en pause et de reprendre des fonctionnalités.

tête:

#import <Foundation/Foundation.h> 

@interface NSTimer (CVPausable) 

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

@end 

Mise en œuvre:

#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 
+0

J'aime les réponses de catégorie! Où est le livre pour ça? – rezwits

0

Eh bien, je trouve cela comme la meilleure méthode pour mettre en œuvre la pause une chose de la minuterie. Utilisez une variable statique pour basculer entre pause et reprise. En pause, invalidez la minuterie et réglez-la à zéro et, dans la section de reprise, réinitialisez la minuterie en utilisant le code original par lequel elle a été démarrée.

Le code suivant fonctionne sur la réponse à un clic sur un bouton de pause.

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

Cela ne fonctionnera pas si l'intervalle est suffisamment long pour être visible. Si votre intervalle est de 10 secondes et que vous arrêtez la minuterie au bout de 5 secondes, le minuteur devrait avoir 5 secondes quand vous le réactivez. Votre solution démarrerait la minuterie à partir de 10 secondes lorsque vous la rallumeriez. –

+0

J'utilise la minuterie pour faire répéter la tâche ne compte pas le temps. Donc, mon cas serait résolu. – divyenduz

-1

J'avais aussi besoin de NSTimer pausable. Après avoir lu ce fil et vos réponses et de réaliser que la seule chose dont j'ai besoin est de régler le fireDate de la minuterie à l'avenir lointain et puis de nouveau à maintenant j'ai fait la catégorie NSTimer avec Pause et Resume méthodes. Je peux donc simplement mettre en pause la minuterie en appelant [myTimer pause]; et reprenez en appelant [myTimer resume]; méthodes Voilà, j'espère que ça va aider quelqu'un.

Interface:

#import <Foundation/Foundation.h> 

@interface NSTimer (Pausable) 

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

@end 

Mise en œuvre:

#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 
+0

Vous devez utiliser [NSDate distantFuture] au lieu de 31536000 – odyth

+0

Oui, bon point, grâce – jano

+0

'dateWithTimeIntervalSinceNow:' 'prend un NSTimeInterval', au lieu d'envoyer ce' [[NSDate distantFuture] timeoutInterval] ' –

2

Sur la base de réponses pour @Thiru et @ kapesoftware, je fait une catégorie Objective-C sur NSTimer utilisant des objets associés pour mettre en pause et reprendre la minuterie.

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

Il garde les choses en ordre et signifie que vous ne devez pas créer des variables ou des propriétés exemple juste pour interrompre et reprendre vos minuteries.

+0

Poussé le projet Xcode à Github https://github.com/tobygundry/PausableTimer, comprend un storyboard de base pour les tests. – Toby

0

Le code ci-dessous est à utiliser avec un scrollview, qui affiche du contenu à intervalles réguliers, et se reboucle à l'avant, mais la même logique peut être utilisée pour tout bouton de pause/redémarrage.La minuterie se déclenche en charge (beginFeatureTimer) et marque une pause pour l'interaction avec l'utilisateur (elle commence à faire glisser la méthode), ce qui permet à l'utilisateur de regarder le contenu aussi longtemps qu'il le souhaite. Lorsque l'utilisateur lève son doigt (finira de glisser), le paramètre booléen featurePaused est réinitialisé, mais vous avez également la possibilité d'ajouter un peu plus de retard. De cette façon, le déflecteur ne se déplace pas instantanément lors de la levée des doigts, il est plus réactif pour l'utilisateur.

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

Si votre intervalle de temps de feu est tolérant, vous pouvez utiliser CADisplayLink au lieu de NSTimer. Parce que CADisplayLink support -pause.

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