美文网首页iOS开发部落代码片段iOS开发转发
在后台执行短信验证码倒计时

在后台执行短信验证码倒计时

作者: 甘邦 | 来源:发表于2017-03-01 11:46 被阅读1506次
    前言

    我们一般做的短信验证码功能,应用在前台运行的时候,肯定是没有问题的,但是点击一下Home键进入后台挂起的状态,我们的定时器就停止了。

    网上很多说记录什么进入后台的时间,我觉的那太费事了,我们完全可以通过申请应用在后台执行来执行我们的倒计时。

    1、先放我们倒计时的代码,这段代码在模拟器运行是没有问题的,但是在真机进入挂起状态会停止

    <pre>

    • (void)sentPhoneCodeTimeMethod {
      //倒计时时间 - 60S
      __block NSInteger timeOut = 59;
      //执行队列
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      //计时器 -》 dispatch_source_set_timer自动生成
      dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
      dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

      dispatch_source_set_event_handler(timer, ^{
      if (timeOut <= 0) {
      dispatch_source_cancel(timer);
      //主线程设置按钮样式
      dispatch_async(dispatch_get_main_queue(), ^{
      // 倒计时结束
      [self.captchaButton setBackgroundImage:kImage(kCaptchaButtonNormal) forState:UIControlStateNormal];
      [self.captchaButton setTitle:kCaptchaButtonTitle forState:UIControlStateNormal];
      [self.captchaButton setEnabled:YES];

                [self.captchaButton setUserInteractionEnabled:YES];
                
            });
        } else {
            //开始计时
            //剩余秒数 seconds
            NSInteger seconds = timeOut % 60;
            NSString *strTime = [NSString stringWithFormat:@"%.1ld", seconds];
            //主线程设置按钮样式
            dispatch_async(dispatch_get_main_queue(), ^{
                [UIView beginAnimations:nil context:nil];
                [UIView setAnimationDuration:1.0];
                
                [self.captchaButton setBackgroundImage:kImage(kCaptchaButtonSelected) forState:UIControlStateNormal];
                NSString *title = [NSString stringWithFormat:@"%@ s",strTime];
                [self.captchaButton setTitle:title forState:UIControlStateNormal];
                
                [UIView commitAnimations];
                //计时器件不允许点击
                [self.captchaButton setUserInteractionEnabled:NO];
                
            });
            timeOut--;
            
        }
      

      });
      dispatch_resume(timer);
      }
      </pre>

    2、下面是我们用到的一个小技巧

    一般来说,如果不进行后台申请,在iOS系统上,当应用退到后台后,只有5s的时间去执行代码,之后将进入挂起状态。只有像音频播放、定位、newsstand、VoIP等功能才能持续在后台运行。但是开发其它应用是我们可以通过申请后台,来获得3分钟的后台执行代码时间(iOS7以前是10分钟)。

    所以我们决定申请3分钟来执行我们1分钟的倒计时代码:
    <pre>

    import "AppDelegate.h"

    @interface AppDelegate (){
    NSInteger count;
    }
    @property(strong, nonatomic)NSTimer *mTimer;
    @property(assign, nonatomic)UIBackgroundTaskIdentifier backIden;

    @end

    @implementation AppDelegate

    • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      // Override point for customization after application launch.

      count=0;
      return YES;
      }

    • (void)applicationDidEnterBackground:(UIApplication *)application {
      _mTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countAction) userInfo:nil repeats:YES];
      [[NSRunLoop currentRunLoop] addTimer:_mTimer forMode:NSRunLoopCommonModes];
      [self beginTask];
      }

    • (void)applicationWillEnterForeground:(UIApplication *)application {
      NSLog(@"进入前台");
      [self endBack];
      }

    //计时
    -(void)countAction{
    NSLog(@"%li",count++);
    }

    //申请后台
    -(void)beginTask
    {
    NSLog(@"begin=============");
    _backIden = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
    //在时间到之前会进入这个block,一般是iOS7及以上是3分钟。按照规范,在这里要手动结束后台,你不写也是会结束的(据说会crash)
    NSLog(@"将要挂起=============");
    [self endBack];
    }];
    }

    //注销后台
    -(void)endBack
    {
    NSLog(@"end=============");
    [[UIApplication sharedApplication] endBackgroundTask:_backIden];
    _backIden = UIBackgroundTaskInvalid;
    }

    @end
    </pre>

    这样就轻松、完美解决了我们的需求了!!!

    3、 扩充无限倒计时

    慎用!因为这个需要申请后台播放音频的权限。如果你的应用不是相关应用,AppStore审核可能不会通过。

    先在我们的info.plist文件配置中,添加一行,如下两个属性:

    Required background modes
    App plays audio or streams audio/video using AirPlay

    配置信息.png

    AppDelegate.m文件中代码还是和上面一样,设置 UIBackgroundTaskIdentifier
    <pre>

    import "AppDelegate.h"

    @interface AppDelegate (){
    NSInteger count;
    }
    @property(strong, nonatomic)NSTimer *mTimer;
    @property(assign, nonatomic)UIBackgroundTaskIdentifier backIden;

    @end

    @implementation AppDelegate

    • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
      // Override point for customization after application launch.

      count=0;
      return YES;
      }

    • (void)applicationDidEnterBackground:(UIApplication *)application {
      _mTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(countAction) userInfo:nil repeats:YES];
      [[NSRunLoop currentRunLoop] addTimer:_mTimer forMode:NSRunLoopCommonModes];
      [self beginTask];
      }

    • (void)applicationWillEnterForeground:(UIApplication *)application {
      NSLog(@"进入前台");
      [self endBack];
      }

    //计时
    -(void)countAction{
    NSLog(@"%li",count++);
    }

    //申请后台
    -(void)beginTask
    {
    NSLog(@"begin=============");
    _backIden = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{

        NSLog(@"将要挂起=============");
        [self endBack];
    }];
    

    }

    //注销后台
    -(void)endBack
    {
    NSLog(@"end=============");
    [[UIApplication sharedApplication] endBackgroundTask:_backIden];
    _backIden = UIBackgroundTaskInvalid;
    }

    @end
    </pre>

    在我们需要后台运行的控制器中,例如ViewController.m
    <pre>

    import "ViewController.h"

    import <AVFoundation/AVFoundation.h>

    @interface ViewController ()

    @property(strong, nonatomic)AVAudioPlayer *mPlayer;

    @property(assign, nonatomic)CGFloat mCount;

    @end

    @implementation ViewController

    • (void)viewDidLoad {
      [super viewDidLoad];
      // Do any additional setup after loading the view, typically from a nib.

      _mCount = 0;

      NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(countTime) userInfo:nil repeats:YES];
      [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
      }

    -(void)countTime{
    _mCount+=10;
    NSLog(@"%f",_mCount);

    if ([[UIApplication sharedApplication] backgroundTimeRemaining] < 60.) {//当剩余时间小于60时,开如播放音乐,并用这个假前台状态再次申请后台
        NSLog(@"播放%@",[NSThread currentThread]);
        [self playMusic];
        //申请后台
        [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
            NSLog(@"我要挂起了");
        }];
    }
    

    }

    -(void)playMusic{
    //1.音频文件的url路径,实际开发中,用无声音乐
    NSURL *url=[[NSBundle mainBundle]URLForResource:@"欢沁.mp3" withExtension:Nil];

    //2.创建播放器(注意:一个AVAudioPlayer只能播放一个url)
    _mPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:Nil];
    
    //3.缓冲
    [_mPlayer prepareToPlay];
    
    //4.播放
    [_mPlayer play];
    

    }

    @end
    </pre>

    完。

    自助者,天助之。

    相关文章

      网友评论

      • 97f52a50453b:我遇到个问题,就是我60s倒计时开始后,我马上退出倒计时界面,然后再进来,页面不能再显示倒计时的变化,但是计时器是正常运行的,这是怎么回事呢
        晓折:我也是,定时器还在计时也在调用,只是更新文本没有生效,再次点击的时候又重新开了另一个计时器
      • 星好唯柔:还有发现你个问题 你的代码里面NSString 直接就跟变量名 * 呢? 兄弟?
        甘邦:哪一行?难道是被编辑器吞掉了?:scream:
      • 星好唯柔:我就想问,这头像是你女票吗?
      • CnnJmh:楼主,麻烦问一下,您的这个定时器,如果遇到界面跳转是否也可以用哪,就是说跳转的别的页面然后再跳转回来的话,是不是会重置?
        甘邦:停止 Dispatch Timer 有两种方法,一种是使用 dispatch_suspend ,另外一种是使用 dispatch_source_cancel,跳转回来的话,你设置一下,把定时器停止。
      • Real_young:有Demo可以看下吗
      • 宋唐不送糖:不错,意识到了问题。想问一下只能在appDelegate中的定时器才能运用的吗
        甘邦:@有种淡淡的忧伤 你说的那个方法那里只是系统在后台的运行时间,并不会说取代什么的。两者没有关系的
        宋唐不送糖:@甘邦 意思是:applicationDidEnterBackground这个方法里面的定时器取代了其他控制器里的倒计时控制器吗?
        甘邦:@有种淡淡的忧伤 不是呢,定时器可以随便放在哪个控制器里:yum:
      • ad9bde090456:学到了。

      本文标题:在后台执行短信验证码倒计时

      本文链接:https://www.haomeiwen.com/subject/preqgttx.html