首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >简单3…2个…iOS应用中的1倒计时

简单3…2个…iOS应用中的1倒计时
EN

Code Review用户
提问于 2015-04-17 10:02:47
回答 2查看 4.2K关注 0票数 9

我想在我的iOS应用上显示一个3.2.1倒计时(在拍照之前),但我没有找到一个优雅的解决方案。我不想用NSTimer制作一个倒计时应用程序。对我来说,重要的是将所有代码保持在相同的方法中,使用像块这样的系统。

代码语言:javascript
复制
self.infoLabel.text   = [NSString stringWithFormat:@"3"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

self.infoLabel.text = [NSString stringWithFormat:@"2"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    self.infoLabel.text = [NSString stringWithFormat:@"1"];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.infoLabel.hidden = YES;


            // DO WHAT I WANT AFTER 3 SECs
        });
    });
});
});

你有更好的解决方案吗?T工作正常,但它并不像我以前那样优雅地连接到dispatch_after上。

EN

回答 2

Code Review用户

发布于 2015-04-19 15:56:19

在任何类型的计时器中,每一个“滴答”都是以前的“滴答”的度量,其问题是,在软件中,这些“滴答”并不一定是精确的。这意味着,如果你的第一个滴答是十分之一秒,剩下的每一个滴答将以这个数量或更多。你的第二个滴答是根据你的第一个滴答排定的。这意味着通过的总时间的实际数量是准确的,而且你拥有的时间越多,你的总时间就越容易出错。

因此,首先,我想向您展示倒计时对象类可能是什么样子。那我来看看我们怎么用这个。

CRCountdown.h.h

代码语言:javascript
复制
typedef void (^CRCountdownCompletion)(void);

@interface CRCountdown : NSObject

- (void)startCountdownWithInterval:(NSTimeInterval)interval 
                             ticks:(NSUInteger)ticks 
                        completion:(CRCountdownCompletion)completion;

@property (readonly) NSUInteger ticksRemaining;
@property (readonly) NSTimeInterval interval;

@end

CRCountdown.m.m

代码语言:javascript
复制
@interface CRCountdown()

@property NSTimer *timer;
@property (readwrite) NSTimeInterval interval;
@property (copy) CRCountdownCompletion completion;

@end

@implementation CRCountdown

- (void)startCountdownWithInterval:(NSTimeInterval)interval ticks:(NSUInteger)ticks completion:(CRCountdownCompletion)completion {
    self.completion = completion;
    self.interval = interval;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:(interval * ticks) target:self selector:@selector(countdownComplete:) userInfo:nil repeats:NO];
}

- (void)stopCountdown {
    [self.timer invalidate];
}

- (NSUInteger)ticksRemaining {
    if (self.timer.isValid) {
        NSTimeInterval timeRemaining = [self.timer.fireDate timeIntervalSinceDate:[NSDate date]];
        return timeRemaining / self.interval;
    } else {
        return 0;
    }
}

- (void)countdownComplete:(NSTimer *)timer {
    if (self.completion) {
        self.completion();
    }
}

@end

现在,这将保证我们的完成事件发生在尽可能准确的时间。我们仍然不能保证它将精确地发生在特定的时刻,但是由于消除了为我们的每一个“更新滴答”增加错误的可能性,错误的范围被大大地减小了。

相反,我们设置了一个重复的NSTimer,负责调用一些代码来更新UI。这个UI更新代码可以查看ticksRemaining (如果有必要的话,将其乘以interval),以确定它应该更新UI来表示什么。

甚至不需要为completion参数传递任何内容,因为我们感兴趣的对象已经在轮询ticksRemaining,因此每当返回0时,我们都知道计时器是完整的(无论如何都是完整的)。

但是,我们可以在Countdown对象上更进一步,并提供一种方法来包含一个update块,如果我们愿意的话,这个块会在每一个滴答上触发。然而,对于这一点,我们想要两位NSTimer的S继续前进。

首先,在我们的标题中,让我们添加另一个块ty对联f:

代码语言:javascript
复制
typedef void (^CRCountdownUpdate)(NSUInteger);

我们还需要更改我们的startCountdown...方法以接受这种类型的参数:

代码语言:javascript
复制
- (void)startCountdownWithInterval:(NSTimerInterval)interval
                             ticks:(NSUInteger)ticks
                            update:(CRCountdownUpdate)update
                        completion:(CRCountdownCompletion)completion;

这意味着在.m中再添加两个属性,一个updateTimer (NSTimer *)和一个CRCountdownUpdate

代码语言:javascript
复制
@property NSTimer *updateTimer;
@property (copy) CRCountdownUpdate update;

我们需要修改我们的countdownComplete:方法,这样它就会使updateTimer失效(所以update永远不会在完成后触发):

代码语言:javascript
复制
- (void)countdownComplete:(NSTimer *)timer {
    [self.updateTimer invalidate];
    if (self.completion) {
        self.completion();
    }
}

并添加一个更新方法来处理更新刻度:

代码语言:javascript
复制
- (void)countdownUpdate:(NSTimer *)timer {
    if (self.update) {
        self.update(self.ticksRemaining);
    }
}

最后一步是修改我们的startCountdown...方法,以匹配我们更新头部的方式,并负责设置update计时器:

代码语言:javascript
复制
- (void)startCountdownWithInterval:(NSTimeInterval)interval ticks:(NSUInteger)ticks completion:(CRCountdownCompletion)completion update:(CRCountdownUpdate)update {
    self.completion = completion;
    self.update = update;
    self.interval = interval;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:(interval * ticks) target:self selector:@selector(countdownComplete:) userInfo:nil repeats:NO];
    if (self.update) {
        self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(countdownUpdate:) userInfo:nil repeats:YES];
    }
}

现在,使用这个对象非常简单:

代码语言:javascript
复制
CRCountdownUpdate update = ^(NSUInteger ticks) {
    self.infoLabel.text = [NSString stringWithFormat:@"%lu", (unsigned long)ticks];
};

CRCountdownCompletion completion = ^{
    self.infoLabel.hidden = YES;
    // do whatever else you want
};

CRCountdown *countdown = [[CRCountdown alloc] init];
[countdown startCountdownWithInterval:1.0
                                ticks:3
                               update:update
                           completion:completion];

我建议的这个课程的最终版本如下所示:

CRCountdown.h

代码语言:javascript
复制
typedef void (^CRCountdownCompletion)(void);
typedef void (^CRCountdownUpdate)(NSUInteger);

@interface CRCountdown : NSObject

- (void)startCountdownWithInterval:(NSTimeInterval)interval ticks:(NSUInteger)ticks completion:(CRCountdownCompletion)completion update:(CRCountdownUpdate)update;
@property (readonly) NSUInteger ticksRemaining;
@property (readonly) NSTimeInterval interval;

@end

CRCountdown.m

代码语言:javascript
复制
@interface CRCountdown()

@property NSTimer *timer;
@property NSTimer *updateTimer;
@property (readwrite) NSTimeInterval interval;
@property (copy) CRCountdownCompletion completion;
@property (copy) CRCountdownUpdate update;

@end

@implementation CRCountdown

- (void)startCountdownWithInterval:(NSTimeInterval)interval ticks:(NSUInteger)ticks completion:(CRCountdownCompletion)completion update:(CRCountdownUpdate)update {
    self.completion = completion;
    self.update = update;
    self.interval = interval;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:(interval * ticks) target:self selector:@selector(countdownComplete:) userInfo:nil repeats:NO];
    if (self.update) {
        self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(countdownUpdate:) userInfo:nil repeats:YES];
    }
}

- (void)stopCountdown {
    [self.updateTimer invalidate];
    [self.timer invalidate];
}

- (NSUInteger)ticksRemaining {
    if (self.timer.isValid) {
        NSTimeInterval timeRemaining = [self.timer.fireDate timeIntervalSinceDate:[NSDate date]];
        return timeRemaining / self.interval;
    } else {
        return 0;
    }
}

- (void)countdownUpdate:(NSTimer *)timer {
    if (self.update) {
        self.update(self.ticksRemaining);
        NSString *s = [NSString stringWithFormat:@"%lu", (unsigned long)self.ticksRemaining];
    }
}

- (void)countdownComplete:(NSTimer *)timer {
    [self.updateTimer invalidate];
    if (self.completion) {
        self.completion();
    }

    self.update = nil;
    self.completion = nil;
}

@end

当然,也可以通过授权而不是阻止来做到这一点,这可能是一种适当的做法。

我已经用基于块和基于委托的方法创建了一个github回购。可以找到这里

票数 8
EN

Code Review用户

发布于 2015-06-04 13:07:36

在单击或在任何方法中启动倒计时,并在那里设置标签并调用方法

代码语言:javascript
复制
self.infoLabel.text = [NSString stringWithFormat:@"3"];
[self performSelector:@selector(countDownMethod) withObject:nil afterDelay:1.0];

现在在类似于

的方法中执行和更改文本

代码语言:javascript
复制
-(void)countDownMethod{

if([self.infoLabel.text isEqualToString:@"3"]){

    self.infoLabel.text   = [NSString stringWithFormat:@"2"];

    [self performSelector:@selector(countDownMethod) withObject:nil afterDelay:1.0];

}else if([self.infoLabel.text isEqualToString:@"2"]){

    self.infoLabel.text   = [NSString stringWithFormat:@"1"];

    [self performSelector:@selector(countDownMethod) withObject:nil afterDelay:1.0];

}else{

    self.infoLabel.hidden = YES;

}

}
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/87169

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档