iOS中一句代码解决倒计时问题

作者: HenryCheng | 来源:发表于2016-01-05 00:10 被阅读19568次

    最近工作比较忙,然后最近也在尝试着翻译一篇关于CALayer非常详解的一篇文章,文章还是比较好也比较长的,等整理完了再发布出来吧。所以也没啥多余的时间写些东西,就先来分享一下开发中的一些小Tips吧。

    一、倒计时问题


    在开发中经常遇到倒计时倒计时问题,写一个Button,然后各种判断各种状态,好多代码感觉很乱,下面就分享一下,一句话解决倒计时问题的例子(当然不是万能的,只适合大部分普通的倒计时_)!
    先看效果

    倒计时按钮的效果

    再看看我们的代码

    //
    //  ViewController.m
    //  HWCountdownDemo
    //
    //  Created by HenryCheng on 16/1/4.
    //  Copyright © 2016年 www.igancao.com. All rights reserved.
    //
    
    #import "ViewController.h"
    #import "UIButton+countDown.h"
    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UIButton *countdownBtn;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
    }
    - (IBAction)countdownBtnClick:(UIButton *)sender {
        [_countdownBtn startWithTime:5 title:@"获取验证码" countDownTitle:@"s" mainColor:[UIColor colorWithRed:84/255.0 green:180/255.0 blue:98/255.0 alpha:1.0f] countColor:[UIColor lightGrayColor]];
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    

    这里主要的就是xib拉了一个button然后连接了它的属性和方法,我们可以看到就调用了

    [_countdownBtn startWithTime:5 title:@"获取验证码" countDownTitle:@"s" mainColor:[UIColor colorWithRed:84/255.0 green:180/255.0 blue:98/255.0 alpha:1.0f] countColor:[UIColor lightGrayColor]];}
    

    这一句代码,就完成了倒计时功能。
    这里我写了一个category,里面代码是这样的

    //
    //  UIButton+countDown.m
    //  LiquoriceDoctorProject
    //
    //  Created by HenryCheng on 15/12/4.
    //  Copyright © 2015年 iMac. All rights reserved.
    //
    
    #import "UIButton+countDown.h"
    
    @implementation UIButton (countDown)
    
    - (void)startWithTime:(NSInteger)timeLine title:(NSString *)title countDownTitle:(NSString *)subTitle mainColor:(UIColor *)mColor countColor:(UIColor *)color {
        
        //倒计时时间
        __block NSInteger timeOut = timeLine;
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        //每秒执行一次
        dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 1.0 * NSEC_PER_SEC, 0);
        dispatch_source_set_event_handler(_timer, ^{
            
            //倒计时结束,关闭
            if (timeOut <= 0) {
                dispatch_source_cancel(_timer);
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.backgroundColor = mColor;
                    [self setTitle:title forState:UIControlStateNormal];
                    self.userInteractionEnabled = YES;
                });
            } else {
                int allTime = (int)timeLine + 1;
                int seconds = timeOut % allTime;     
                NSString *timeStr = [NSString stringWithFormat:@"%0.2d", seconds];
                dispatch_async(dispatch_get_main_queue(), ^{
                    self.backgroundColor = color;
                    [self setTitle:[NSString stringWithFormat:@"%@%@",timeStr,subTitle] forState:UIControlStateNormal];
                    self.userInteractionEnabled = NO;
                });
                timeOut--;
            }
        });
        dispatch_resume(_timer);
    }
    
    @end
    

    关于这个方法的定义

    //
    //  UIButton+countDown.h
    //  LiquoriceDoctorProject
    //
    //  Created by HenryCheng on 15/12/4.
    //  Copyright © 2015年 iMac. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @interface UIButton (countDown)
    
    /**
     *  倒计时按钮
     *
     *  @param timeLine 倒计时总时间
     *  @param title    还没倒计时的title
     *  @param subTitle 倒计时中的子名字,如时、分
     *  @param mColor   还没倒计时的颜色
     *  @param color    倒计时中的颜色
     */
    - (void)startWithTime:(NSInteger)timeLine title:(NSString *)title countDownTitle:(NSString *)subTitle mainColor:(UIColor *)mColor countColor:(UIColor *)color;
    
    @end
    

    试想,如果你有多个界面用到这样的倒计时按钮,比如什么登录注册、修改密码啥的,直接调用一个方法,会不会很方便?
    上面的Demo所有的代码可以在 这里 看到
    当然,这里只是简单地自定义,你还可以在里面做更多的操作,比如加点动画什么的。之前写过Swift的一些倒计时的例子,如果你有兴趣,可以看看下面的效果

    加动画的倒计时按钮
    代码在这里可以看到

    二、复合语句在 Objective-C 中的使用


    之前在一篇文章中看到过一次介绍复合语句在iOS中的使用,这里跟大家分享一下。
    比如我们一般写一个tableView一般都是向下面这种写法写的

            self.myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame)) style:UITableViewStyleGrouped];
            self.myTableView.dataSource = self;
            self.myTableView.delegate = self;
            [self.view addSubview:self.myTableView];
    

    使用复合语句的话就是把整个代码块放在{里面,看起来更清晰,如下

       self.myTableView = ({ UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame)) style:UITableViewStyleGrouped];
            tableView.dataSource = self;
            tableView.delegate = self;
            [self.view addSubview:tableView];
            tableView;
           });
    

    其实上面两段代码意思完全一样,只不过写法不同罢了,第二段看起来更炫酷,快去试试吧!

    相关文章

      网友评论

      • 孤雁_南飞:怎么在后台也运行呀!
      • 97f52a50453b:我遇到个问题,就是我60s倒计时开始后,我马上退出倒计时界面,然后再进来,页面不能再显示倒计时的变化,但是计时器是正常运行的,这是怎么回事呢
      • 小花猫走路静悄悄:真机下,按home键退出APP,计时器就停止运行了。
      • 丶逝水流年:大神,问一下,我是从控制器1底部弹出的控制器2 uiviewcontroller ,然后我点击按钮 按钮倒计时中,我点击返回 回到控制器1 再进入控制器2 那么按钮的倒计时没了,我是用的你的分类方法,我要咋样搞才能出去再进来还显示倒计时啊
      • Ju独一: 但是题主,有个问题哦,在获取验证码,可以回退在进入,这个倒计时就归零了,会导致有人利用这个刷验证码哦。
        0d48debc0995:@HenryCheng 利用runloop Model 吗?
        HenryCheng:@Ju独一 这个吧一般是后台在判断的,做出相应处理,不过本地这儿做也可以,这个demo里面是没做这个处理的
      • 汉斯哈哈哈:在iOS7上无法更新title, 设置title时加上[button setNeedsLayout];,应该是apple bug
        HenryCheng:@汉斯哈哈哈 这个还没在iOS 7上试过,iOS 7好像有很多得特殊处理,比如你把一个button的title的字体设置为bold就会有问题,和iOS 8不一样,谢谢指正
      • TankCh:完全不行..你1秒卡住
      • 一个人的阳光:GCD用得挺溜的,哪里可以看到GCD的用法,感觉这方面还很弱,楼主是看书学的还是看苹果的GCD使用文档 :smiley:
      • __Jason__:怎么取消倒计时呢?
      • 不是谢志伟:学习了
      • Ennnnnn7:如果退出当前控制器 如何移除timer
      • 永远都能:@HenryCheng ViewController关闭了定时器还在跑哦,建议加上这个判断:
        if (timeOut <= 0||!weakSelf) {...};
      • 易明:感谢分享!
      • 78bd5ca7ec3f:谢谢 O(∩_∩)O
        HenryCheng:@meam 哈哈,对你有帮助就好
      • 我就叫土豆:测试了一下,调整为120S,但是按钮却显示00S->59S...也就是说大于60秒不能显示,显示也是从59秒开始倒计时...
        HenryCheng:@我就叫土豆 新的代码里面已经有了,使用weakself可以防止闭环,新的代码都优化了,你可以下载看看
        我就叫土豆:@HenryCheng github上面是swift版本的.我已经改好了.
        但是我看到评论中:
        编程的蚂蚁
        23 楼 · 2016.01.06 14:57
        另外那个block里面也不建议直接使用self,用__weak __typeof(&*self)weakSelf =self;替代会更完美.

        指的是修改所有的self吗?还是其他的改法
        HenryCheng:@我就叫土豆 是的,你是复制博客上的代码实验的吧,这个bug在很早之前就修复了,是我没有考虑到的,在我的github上你可以下载最新的代码,已经修复了这个问题,谢谢建议!
      • 硅谷小虾米:原来是将倒计时函数进行封装然后引用啊,我还以为类本身就带有这个方法
        HenryCheng:@124223 本身有这种肯定早就被拿出来用了。。。
      • Theyouth:记录
      • QingQueen:每次看你的博客都会收获良多,赞一个:+1:
        HenryCheng:@ASpume 谢谢支持 :blush:
      • pengrain:我们项目里面有个倒计时功能,类似于彩票开奖的倒计时,倒计时时间很长,而且倒计时的个数也很多,开始用gcd来写这个倒计时 发现一下子点开后十几个线程都在跑,还停不下来 瞬间奔溃
      • I_m赵昊:楼主为何开这么多线程,能指点下么
      • long855:楼主 我用 xib 拉的button 会有闪动的效果 qq 471727371
        HenryCheng:@long855 因为你用代码写的时候写的是custom的吧,xib拖的时候默认的是system,所以他会出现闪动
        HenryCheng:@long855 因为你的button是system的,改成custom的就好
        long855:@long855
        不用xib 直接创建的没有闪动
      • 瑾仁:为甚么会有这么多线程在里面?它实现的效果与定时器实现效果有什么区别麽?
      • 柚子橙黄:复合语句,get:kissing_heart:
        HenryCheng:@iOS卡卡_微博 :relaxed:
      • _Shj:啥是GCC啊 ?
        :relieved:
        HenryCheng:@SShj :blush: www.baidu.com
      • 编程的蚂蚁:另外那个block里面也不建议直接使用self,用__weak __typeof(&*self)weakSelf =self;替代会更完美.
        霖溦:@编程的蚂蚁 gcd以及系统动画的block都没有必要weak,系统会将该block置空
        我就叫土豆:@编程的蚂蚁 蚂蚁哥,是需要将block里面的所有的self 改成 :__weak __typeof(&*self)weakSelf =self;这个吗?还是怎么改?
        HenryCheng:@编程的蚂蚁 嗯,使用weak能确保不会Retain Cycle,更合理一些 :+1:
      • 编程的蚂蚁:写的不错,受教了,有一点我觉得有点不太妥当,int seconds = timeOut % 60;这句话,最好改成
        int allTime = (int)timeLine+1; int seconds = timeOut % allTime;这两句,这样倒计时就可以更通用了,只是建议 :blush:
        HenryCheng:@编程的蚂蚁 谢谢你的意见 :clap:
      • Fe_Zn:如果用weak 修饰tableView ,用复合语句的话会有⚠,怎么解决呢?
        HenryCheng:@JsonChan 你用weak跟用不用复合语句没有关系。。。。。。
        Fe_Zn:@HenryCheng 对的,object will be release after assignment..不过运行中并没有什么问题,只是单纯看那个⚠️有点不爽... :joy: :joy:
        HenryCheng:@JsonChan 警告是分配完将会被释放?
      • 42b702b6b0eb:复合写法有点意思
      • 07a0b9addc9e:复合语句的最后一行tableView是什么意思。release吗?
        HenryCheng:@疾风哥哥 不是,复合语句最后一个语句的值就是跟返回值差不多的意思,比如这里self.myTableView = 整个复合语句最后一个语句,也就是tableView
      • johnmuu:get到了😁
        HenryCheng:@johnmuu :+1:
      • 667a69f9a9e1:不想不错
        HenryCheng:@667a69f9a9e1 :pray:
      • 花前月下:涨姿势了。 学到了复合语句。
        HenryCheng:@花前月下 :sunglasses:
      • 卖艺小青年o0:开拓思维
      • Zz7777777:不错
      • 96dfced25580:复合写法很不错,尤其是在自定义复杂view的时候,能够区分开来,不过现在都习惯自己分隔几行来区分各个子view的初始化,知道得太晚了 :sob:
        sclcoder:@HenryCheng 一般我都是懒加载写在get方法里, 复合写法也不错
        HenryCheng:@仲系xbb 能意识到晚的其实都不晚 :blush:
      • b09e41988821:留个脚印,code已带走:smile:
      • AdhereDamon:为什么添加那么多线程呢!
        张智杰:@KWK 在子线程里计算时间,在主线程里更新UI
        AdhereDamon:@lan941025 定时器默认就是开启一个线程的,但博主这么写为什么,具体我也不懂!
        瑾仁:@KWK 我也想问这个问题?难道定时器不能实现这个效果麽?
      • hcname:简单的就是好
      • 深深浅浅的刀割:完全被楼主的标题骗进来了,楼主好样的:+1:
      • 纯洁的小袋子:不错,学习了
      • Mokyz:说好的一句话解决呢? :joy:
        HenryCheng:@Mokz 调用的时候就是一句代码啊😓
      • 曾樑:这个实用的
        HenryCheng:@曾樑 嗯,对一般的倒计时问题足够的

      本文标题:iOS中一句代码解决倒计时问题

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