objective-c 如何以编程方式暂停 NSTimer?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/347219/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I programmatically pause an NSTimer?
提问by MrDatabase
I'm using an NSTimer to do some rendering in an OpenGL based iPhone app. I have a modal dialog box that pops up and requests user input. While the user is providing input I'd like to "pause" i.e. something like this:
我正在使用 NSTimer 在基于 OpenGL 的 iPhone 应用程序中进行一些渲染。我有一个弹出并请求用户输入的模式对话框。当用户提供输入时,我想“暂停”,即像这样:
[myNSTimer pause];
I'm using this syntax because I've been doing things like:
我使用这种语法是因为我一直在做这样的事情:
[myNSTimer invalidate];
when I want it to stop.
当我想要它停止时。
How can I programmatically pause the NSTimer?
如何以编程方式暂停 NSTimer?
采纳答案by BobbyShaftoe
From here:
从这里:
http://discussions.apple.com/thread.jspa?threadID=1811475&tstart=75
http://discussions.apple.com/thread.jspa?threadID=1811475&tstart=75
"You can store the amount of time that has passed since the timer started... When the timer starts store the date in an NSDate variable. Then when the user switches... use the method timeIntervalSinceNow in the NSDate class to store how much time has passed... note that this will give a negative value for timeIntervalSinceNow. When the user returns use that value to set an appropriate timer.
“您可以存储自定时器启动以来经过的时间量......当定时器开始时将日期存储在 NSDate 变量中。然后当用户切换时......使用 NSDate 类中的 timeIntervalSinceNow 方法来存储多少时间已经过去...请注意,这将为 timeIntervalSinceNow 提供负值。当用户返回时,使用该值来设置适当的计时器。
I don't think there's a way to pause and restart a timer. I faced a similar situation. "
我认为没有办法暂停和重新启动计时器。我遇到了类似的情况。”
回答by Thiru
Just thought of updating minor fixes to kapesoftware's answer:
只是想更新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];
}
回答by kapesoftware
The way I accomplished this was by storing the NSTimer's firing date in a variable and then setting the firing date to INFINITY.
我完成此操作的方法是将 NSTimer 的触发日期存储在一个变量中,然后将触发日期设置为 INFINITY。
When I pause:
当我暂停时:
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFiringDate = [timer firingDate];
[timer setFiringDate:INFINITY]
When I unpause I add the amount of time that the timer was paused to the previous firing date:
当我取消暂停时,我将计时器暂停的时间添加到上一个触发日期:
float pauseTime = -1*[pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
This made it a lot easier than worrying about invalidating and reinitializing the timer. I had a lot of timers so I just put them all in an array and did this to all of them. I subclassed the NSTimer in order to keep the previousFiringDate associated with that timer.
这比担心使计时器失效和重新初始化要容易得多。我有很多计时器,所以我只是将它们全部放在一个数组中,然后对所有计时器执行此操作。我对 NSTimer 进行了子类化,以保持与该计时器关联的 previousFiringDate。
This was also better than using a BOOL of whether or not it was paused so actions wouldn't take place if the BOOL was set. This is because if something was about to happen before you pause, it won't happen after you unpause it because it will have just been skipped.
这也比使用是否暂停的 BOOL 更好,因此如果设置了 BOOL,则不会发生操作。这是因为如果某件事在您暂停之前即将发生,那么在您取消暂停之后它就不会发生,因为它只是被跳过了。
回答by Riley
The easiest way I managed to do it was like this:
我设法做到的最简单的方法是这样的:
-(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;
}
}
回答by Riley
In my case, I had a variable 'seconds', and another 'timerIsEnabled'. When I wanted to pause the timer, just made the timerIsEnabled as false. Since the seconds variable was only incremented if timerIsEnabled was true, I fixed my problem. Hope this helps.
就我而言,我有一个变量“seconds”和另一个“timerIsEnabled”。当我想暂停计时器时,只需将 timerIsEnabled 设为 false。由于仅当 timerIsEnabled 为 true 时, seconds 变量才会增加,因此我解决了我的问题。希望这可以帮助。
回答by Carl Veazey
Here is a category on NSTimerthat allows pause and resume functionality.
这是一个NSTimer允许暂停和恢复功能的类别。
Header:
标题:
#import <Foundation/Foundation.h>
@interface NSTimer (CVPausable)
- (void)pauseOrResume;
- (BOOL)isPaused;
@end
Implementation:
执行:
#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
回答by Toby
Based off @Thiru and @kapesoftware's answers, I made an Objective-C category on NSTimer using Associated Objects for pausing and resuming the timer.
根据@Thiru 和@kapesoftware 的回答,我在 NSTimer 上创建了一个 Objective-C 类别,使用关联对象来暂停和恢复计时器。
#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);
}
It keeps things tidy and means you don't have to create instance variables or properties just to pause and resume your timers.
它使事情保持整洁,意味着您不必为了暂停和恢复计时器而创建实例变量或属性。
回答by Chris
Old question, but I just wrote a lightweight utility class to accomplish exactly what OP is looking for.
老问题,但我只是写了一个轻量级的实用程序类来完成 OP 正在寻找的内容。
https://github.com/codeslaw/CSPausibleTimer
https://github.com/codeslaw/CSPausibleTimer
Supports repeating timers as well. Hope it comes in handy!
也支持重复计时器。希望它派上用场!
回答by keeshux
Feel free to reuse my category on NSTimer:
随意重用我的类别NSTimer:
https://github.com/keeshux/ios-components/tree/master/Components/Categories/NSTimer%2BPause
https://github.com/keeshux/ios-components/tree/master/Components/Categories/NSTimer%2BPause
回答by Johnny Rockex
The code below is for use with a scrollview, that brings content on at regular intervals, and loops back to the front on completion, but the same logic can be used for any pause/restart button.
下面的代码用于滚动视图,它定期显示内容,并在完成时循环回到前面,但相同的逻辑可用于任何暂停/重新启动按钮。
The timer fires on load (initiateFeatureTimer), and pauses for user-interaction (will begin dragging method), allowing the user to look at content for as long as she likes. When user lifts her finger (will end dragging) the featurePaused boolean is reset, but also, you have the option of adding in a little more of a delay.
计时器在加载时触发(initiateFeatureTimer),并暂停用户交互(将开始拖动方法),允许用户查看内容,只要她喜欢。当用户抬起她的手指(将结束拖动)时,featurePaused 布尔值被重置,而且,您还可以选择增加一点延迟。
This way, the scroller doesn't move off instantly on finger lift, it's more reactive to the user.
这样,滚动条不会在手指抬起时立即移动,而是对用户更具反应性。
//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
}

