iOS 短信验证码倒计时按钮

级别: ★★☆☆☆
标签:「iOS 验证码后台倒计时」「NSTimer后台运行」「iOS 定时器后台运行」
做者: Xs·H
审校: QiShare团队php


短信验证码登陆在app中十分常见,相对于帐号+密码的登陆方式,短信验证码登陆既免去了用户记忆密码的繁琐,也在很大程度上下降了密码泄露的风险。可是,对app运营方来讲,每发一条短信就会支付相对应的短信费,因此,为了防止恶意频繁访问,在设计发送短信验证码接口时会加上限制逻辑。好比,针对同一手机号,接口会控制在120秒内不可重复发送短信验证码。为了优化用户体验,app每每会作出对应的逻辑控制:在点击“获取验证码”按钮后将按钮设置成不可点击状态,并开始120秒的倒计时,倒计时结束后恢复按钮为可点击状态。git

按照上述需求,实现一个倒计时按钮并不难,使用NSTimer就能够。但因为在app进入后台时NSTimer会被暂停,直到app进入前台,NSTimer才会继续工做(若是NSTimer还没被释放的话),这就会致使按钮上的倒计时会缺失app在后台的那段时间。因此,开发者要想办法补上这段时间。github

如何补上这段时间,就是本文探讨的内容。在此以前,先经过下图看一下短信验证码倒计时场景。算法

要补上app进入后台的时间,有两个思路:bash

    1. 记录下app在后台的时间,在app进入前台后补给定时器;
    1. 想办法让NSTimer在app进入后台后可以运行120秒以上。
思路1:记录app在后台的时间

经过监听UIApplicationDidEnterBackgroundNotificationUIApplicationWillEnterForegroundNotification能够获取到app进入后台和回到前台的时间戳,将这两个时间戳取差,就是app在后台的时间,而后用这个时间对计时器的时间进行补偿就能实现需求。代码以下:微信

#pragma mark - Notifications

- (void)applicationDidEnterBackground:(id)sender {
    NSLog(@"%s", __func__);
    
    _didEnterBackgroundTimestamp = [[NSDate date] timeIntervalSince1970];
}

- (void)applicationWillEnterForeground:(id)sender {
    NSLog(@"%s", __func__);
    
    NSTimeInterval willEnterForegroundTimestamp = [[NSDate date] timeIntervalSince1970];
    
    NSInteger onBackgroundSeconds = floor((_didEnterBackgroundTimestamp == 0)? 0: (willEnterForegroundTimestamp - _didEnterBackgroundTimestamp));
    _currentInteger -= onBackgroundSeconds;
}
复制代码
  • didEnterBackgroundTimestamp是全局变量,用来记录app进入后台时的时间戳;
  • onBackgroundSeconds表示app在后台的时间(秒数)。用三目运算排除app直接启动时的状况;
  • 用floor()函数对Double类型的时间差进行向下取整,是为了保证倒计时不会提早结束。
思路2:使NSTimer在后台保持运行

首先,Apple容许开发者向系统申请后台运行权限,好比使用“位置服务”的“高德地图”、“滴滴出行”等app和使用“音频服务”的“QQ音乐”、“网易云音乐”等app。但若是申请了权限,就得保证app提供相应的服务,否则在上线App Store时会被拒绝。因此,对于简单的倒计时场景,不考虑使用这种方式。 好在,iOS向开发者提供了临时借用后台运行权限的API,以向app提供最多180秒的后台运行权限。说到“借用”,就得有个“借条”(凭证),还得有借有还。 经过监听UIApplicationDidEnterBackgroundNotification,在app进入后台时,调用API向系统借用180秒的后台运行权限,并保留借用凭证。在借用时间即将到期时或者作完须要作的事情后调用API将后台权限还给系统,并将借用凭证标识为失效状态。具体代码以下。app

- (void)stopCountdown {
    
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
    
    [self endBackgroundTask];
    [self setEnabled:YES];
    
    [_timer invalidate];
    _timer = nil;
}


#pragma mark - Private functions

- (void)startBackgroundTask {
    
    __weak typeof(self) weakSelf = self;
    _backgroundTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
        [weakSelf endBackgroundTask];
    }];
}

- (void)endBackgroundTask {
    
    [[UIApplication sharedApplication] endBackgroundTask:self.backgroundTaskId];
    _backgroundTaskId = UIBackgroundTaskInvalid;
}


#pragma mark - Notifications

- (void)applicationDidEnterBackground:(id)sender {
    NSLog(@"%s", __func__);
    
    [self startBackgroundTask];
}
复制代码
  • backgroundTaskId是全局变量,表示向系统借用后台权限所产生的凭证;
  • beginBackgroundTaskWithExpirationHandler是借用的后台权限将到期时会触发的block,在里面要作“还权限”的操做;
  • 在定时器倒计时结束后,会调用stopCountdown方法,在里面提早执行“还权限”的操做。

以上是做者实现短信验证码倒计时按钮经常使用到的两种方式。为了方便复用,做者将倒计时功能封装进了按钮中,工程代码可从QiCountdownButton中获取。函数


小编微信:可加并拉入《QiShare技术交流群》。优化

关注咱们的途径有:
QiShare(简书)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公众号)ui

推荐文章:
iOS 环境变量配置
iOS 中处理定时任务的经常使用方法
算法小专栏:贪心算法
iOS 快速实现分页界面的搭建
iOS 中的界面旋转
奇舞周刊

相关文章
相关标签/搜索