it-swarm.com.de

Wie kann ich einen NSTimer programmgesteuert anhalten?

Ich verwende einen NSTimer, um in einer OpenGL-basierten iPhone-App etwas zu rendern. Ich habe ein modales Dialogfeld, das auftaucht und Benutzereingaben anfordert. Während der Benutzer Eingaben macht, möchte ich "pausieren", d. H.

[myNSTimer pause];

Ich verwende diese Syntax, weil ich Folgendes getan habe:

[myNSTimer invalidate];

wenn ich will, dass es aufhört.

Wie kann ich den NSTimer programmgesteuert anhalten?

42
MrDatabase

Von hier:

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

"Sie können die Zeit speichern, die seit dem Start des Timers vergangen ist ... Wenn der Timer startet, speichern Sie das Datum in einer NSDate-Variablen. Wenn der Benutzer wechselt, verwenden Sie die Methode timeIntervalSinceNow in der NSDate-Klasse, um wie viel zu speichern Zeit ist vergangen ... Beachten Sie, dass dies einen negativen Wert für timeIntervalSinceNow ergibt.Wenn der Benutzer zurückkehrt, verwenden Sie diesen Wert, um einen geeigneten Timer einzustellen.

Ich glaube nicht, dass es eine Möglichkeit gibt, einen Timer anzuhalten und erneut zu starten. Ich war in einer ähnlichen Situation. "

22
BobbyShaftoe

Denken Sie nur daran, kleinere Korrekturen an der Antwort von kapesoftware zu aktualisieren:

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

Ich habe das erreicht, indem ich das Brenndatum des NSTimer in einer Variablen gespeichert und dann das Brenndatum auf INFINITY gesetzt habe. 

Wenn ich pausiere:

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

Wenn ich die Pause unterbreche, füge ich die Zeit hinzu, zu der der Timer zum vorherigen Brenndatum angehalten wurde:

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

Dies machte es viel einfacher als sich um die Ungültigmachung und Neuinitialisierung des Timers zu sorgen. Ich hatte viele Timer, also habe ich sie alle in ein Array gestellt und dies mit allen gemacht. Ich habe den NSTimer untergeordnet, um das vorherige FiringDate mit diesem Timer zu verknüpfen.

Dies war auch besser als die Verwendung eines BOOLs, ob es pausiert war oder nicht, so dass keine Aktionen stattfinden würden, wenn der BOOL gesetzt wurde. Dies liegt daran, dass, wenn etwas vor der Pause geschehen sollte, dies nicht geschieht, nachdem Sie es unterbrochen haben, da es gerade übersprungen wurde.

12
kapesoftware

Der einfachste Weg, wie ich es geschafft habe, war so:

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

In meinem Fall hatte ich eine Variable "Sekunden" und eine andere "TimerIsEnabled". Als ich den Timer anhalten wollte, habe ich einfach timerIsEnabled als false gesetzt. Da die Sekunden-Variable nur erhöht wurde, wenn timerIsEnabled den Wert true hat, wurde das Problem behoben. Hoffe das hilft.

8
Oscar Olim

Hier ist eine Kategorie zu NSTimer, die die Funktion zum Anhalten und Fortsetzen ermöglicht.

Header:

#import <Foundation/Foundation.h>

@interface NSTimer (CVPausable)

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

@end

Implementierung:

#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
5
Carl Veazey

Alte Frage, aber ich habe gerade eine leichte Utility-Klasse geschrieben, um genau das zu erreichen, wonach OP sucht.

https://github.com/codeslaw/CSPausibleTimer

Unterstützt auch die Wiederholung von Timern. Hoffe, es ist praktisch!

2
Chris

Basierend auf den Antworten von @Thiru und @ kapesoftware habe ich mit NSTimer eine Objective-C-Kategorie unter Verwendung von verknüpften Objekten erstellt, um den Timer anzuhalten und fortzusetzen.

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

Es hält die Dinge aufgeräumt und bedeutet, dass Sie keine Instanzvariablen oder -eigenschaften erstellen müssen, nur um die Timer anzuhalten und wieder aufzunehmen. 

2
Toby

Fühlen Sie sich frei, meine Kategorie auf NSTimer wiederzuverwenden:

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

1
keeshux

Wenn Ihr Feuerzeitintervall tolerant ist, können Sie CADisplayLink anstelle von NSTimer verwenden. Weil CADisplayLink-pause unterstützt.

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

Sie können NSTimer wie oben erwähnt nicht anhalten. Die Zeit, die erfasst werden soll, sollte also nicht vom Timer-Feuer abhängen, schlage ich vor. Hier ist meine einfachste Lösung:

Beim Erstellen des Timers initialisieren Sie die Startzeit wie folgt:

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

` Nun in der Update-Timer-Methode:

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

 }
0
Masud alam

Nun, ich finde dies als die beste Methode, um das Anhalten einer Timer-Sache zu implementieren. Verwenden Sie eine statische Variable, um die Pause umzusetzen und fortzusetzen. Deaktivieren Sie in der Pause den Timer und setzen Sie ihn auf null. Setzen Sie den Timer im Wiederaufnahmeabschnitt mit dem ursprünglichen Code zurück, mit dem er gestartet wurde.

Der folgende Code funktioniert für die Antwort auf einen Pausenschaltflächenklick.

-(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
divyenduz

Der folgende Code ist für die Verwendung mit einer Bildlaufansicht gedacht, die den Inhalt in regelmäßigen Abständen einschaltet und nach Abschluss des Vorgangs wieder in den Vordergrund zurückschaltet. Dieselbe Logik kann jedoch für jede Pause/Neustart-Schaltfläche verwendet werden. 

Der Timer wird beim Laden ausgelöst (initiateFeatureTimer) und pausiert für die Benutzerinteraktion (beginnt mit dem Ziehen der Methode), sodass der Benutzer den Inhalt so lange betrachten kann, wie er möchte. Wenn der Benutzer den Finger anhebt (wird mit dem Ziehen beendet), wird das FeaturePauseed boolean zurückgesetzt. Sie haben jedoch auch die Möglichkeit, eine Verzögerung hinzuzufügen.

Auf diese Weise fährt der Scroller beim Anheben der Finger nicht sofort los, er reagiert schneller auf den Benutzer.

//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
Johnny Rockex

Hier ist ein Simple Swift-Timer für Pause/Wiedergabe.

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

0
Aggressor