1.写在前面[可以不用看]:
关于从前到现在写倒计时按钮的一些经历
最初:写的倒计时按钮就是用timer实现的,然后在周期触发的函数里面将记录的秒数 _second--, 这样做是可以解决简单的需求的。但是如果App在倒计时的时候进入到后台。此时倒计时的timer就不会执行了。只有当App重新从后台切换到前台的时候 timer才会继续执行。这样记录的时间显然就错了。因为没有减去中间在后台的那部分时间。
中期:由于初期遇到的问题,后来我想既然如此 就记录用户在进入后台时的时间A,然后进入前台时记录此时的时间B, 通过计算这两个时间的时间差,然后用此时倒计时的时间减去这个时间差来精确倒计时。 这样的确可以解决问题。 恶心的事情就是 还要去监听App前台后台的切换,还要定义多个变量去计算这个时间差。。。
后来:就是现在本文使用的方法,其实大致和中期的差不多,只是省略了计算前后台时间差的步骤。
原理:在timer初始化的时候记录了一个时间A,然后在周期触发的函数中每次都会获取一遍当前的时间,通过计算时间A到当前时间所花费的时间B,然后用倒计时的总时间减去这个时间B 就得到了倒计时剩余的时间。这样就可以达到要求了。而且实现起来也很简单。
最后说一下内存释放的问题:大家都知道 在使用timer的时候 有意个不好的地方就是在不使用的时候 如果不主动调用 [timer invalidate] 该时间对象是不会释放的。所以我这里也会去调用这个方法来释放内存。
封装的代码里面 会在倒计时技术的时候 自动调用一遍 [timer invalidate] ,及时用户当前界面pop了也没有关系,因为timer还在继续执行[这个地方貌似更runloop的机制有关,管兴趣的朋友可以百度 或者 google]
封装的方法里面也提供了主动释放资源的方法:cleanTimer
2:show my code 正题
头文件
#import <UIKit/UIKit.h>
/**
关于资源释放的问题:不用外界手动调用[timer invalidate];
由于定时器开启之后倒计时到0时就会调用[timer invalidate],及时在界面pop之后也一样。到时候资源就可以自动释放了
*/
@interface FXW_CountDownButton :UIButton
/**
设置倒计时时间不设置的话 模式是60s
*/
@property (nonatomic,assign) NSInteger countDownSecond;
/**
定义代码块
@param isStartCounting 开始倒计时
@param isCounting 正在倒计时
*/
typedef void (^FXW_CountDownButtonBlock)(BOOL isStartCounting,BOOL isCounting);
/**
点击事件回调
@param block FXW_CountDownButtonBlock
*/
- (void)clickButtonWithResultBlock:(FXW_CountDownButtonBlock)block;
/**
如果想要提前关闭定时器可以调用该方法。不是必要的操作
*/
- (void)cleanTimer;
@end
.m文件
#import "FXW_CountDownButton.h"
@interface FXW_CountDownButton()
@property (nonatomic,copy) FXW_CountDownButtonBlock clickButtonCallback;
@property (nonatomic,assign) NSTimeInterval timeNum;
@property (nonatomic,strong) NSTimer *timer;
@property (nonatomic,strong) NSDate *startDate;
@end
@implementation FXW_CountDownButton
- (id)initWithFrame:(CGRect)frame
{
self = [superinitWithFrame:frame];
if (self)
{
//可以给不同的事件设置不同的block
[selfaddTarget:selfaction:@selector(clickAction)forControlEvents:UIControlEventTouchUpInside];
_countDownSecond =60;//设置默认倒计时时间为 60s
}
returnself;
}
#pragma mark 开始倒计时
- (void)startCountDown
{
if (_timeNum <=0)
{
_timeNum =_countDownSecond;//设置默认倒计时时间为60s
}
if (_timer ==nil)//设置倒计时
{
//每间隔1s会去执行一次countDown
_timer = [NSTimerscheduledTimerWithTimeInterval:1
target:self
selector:@selector(countDown)userInfo:nilrepeats:YES];
_startDate = [NSDatedate];//记录倒计时时的时间
// [[NSRunLoop currentRunLoop]addTimer:_timer forMode:NSRunLoopCommonModes];
}
[selfsetTitle:[NSStringstringWithFormat:@"还剩%d秒", (int)_timeNum]forState:UIControlStateNormal];
}
#pragma mark 倒计时_周期调用 <=0时停止倒计时 并恢复默认样式
- (void)countDown
{
double deltaTime = [[NSDatedate] timeIntervalSinceDate:_startDate];//计算从开始倒计时到此刻的时间差
_timeNum =_countDownSecond - (NSInteger)(deltaTime+0.5);
[selfsetTitle:[NSStringstringWithFormat:@"还剩%d秒", (int)_timeNum]forState:UIControlStateNormal];
if (_timeNum <=0)
{
[selfsetTitle:@"重新获取验证码"forState:UIControlStateNormal];
_timeNum =_countDownSecond;
[_timerinvalidate];
_timer =nil;
}
}
- (void)clickAction
{
if (_clickButtonCallback)
{
if (_timeNum <=0 || _timeNum ==60)
{
[selfstartCountDown];
_clickButtonCallback(YES,NO);
}
else//如果还在倒计时则不执行新的倒计时
{
NSLog(@"倒计时还在继续。。。");
_clickButtonCallback(NO,YES);
}
}
}
- (void)clickButtonWithResultBlock:(FXW_CountDownButtonBlock)block
{
_clickButtonCallback = block;
}
- (void)cleanTimer
{
[_timerinvalidate];
_timer =nil;
}
- (void)dealloc
{
NSLog(@"倒计时按钮资源已释放");
}
@end
下面感谢开源项目提供者:https://github.com/shaojiankui/JKCountDownButton 为我提供的思路
下面写下我的项目地址:https://github.com/wokenshin/KenshinPro