美文网首页iOS经验总结
iOS开发-倒计时功能

iOS开发-倒计时功能

作者: 铁头娃_e245 | 来源:发表于2019-03-01 15:56 被阅读0次

    倒计时是一个常用的功能页面, 该功能可以分解成3步: ①倒计时UI的展示 ②定时数据的格式与解析 ③定时器的选择

    下面通过3个知识点来完成整个功能

    1. UIDatePicker

    UIDatePicker 是一个系统封装好控制器类,封装了 UIPickerView,但是他是UIControl的子类,专门用于接受日期、时间和持续时长的输入,我们对于时间选择的UI可以直接使用该类去完成,下面贴出具体使用代码

    @property (nonatomic, strong) UIDatePicker *datePicker;   //设置为属性
    
    - (void)configPickerView{    //创建
        UIDatePicker *datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0, 100, self.view.frame.size.width, 200)];
        
        //设置地区: zh-中国
        datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"zh"];
        
        //设置日期模式(Displays month, day, and year depending on the locale setting)
        datePicker.datePickerMode = UIDatePickerModeCountDownTimer;
        
        //设置当前显示时间
        //需要转换的字符串
        NSString *dateString = @"5";   //格式2018-11-22 08:08:08对应yyyy-MM-dd HH:mm:ss   初始化为5
        //设置转换格式
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
        [formatter setDateFormat:@"mm"];  //对应设置的5为分钟
        //NSString转NSDate
        NSDate *date=[formatter dateFromString:dateString];
        
        [datePicker setDate:date animated:YES];
        
        //监听DataPicker的滚动
        [datePicker addTarget:self action:@selector(dateChange:) forControlEvents:UIControlEventValueChanged];
        
        self.datePicker = datePicker;
        
        [self.view addSubview:self.datePicker];
    }
    

    这里需要注意datePickerMode方法,系统提供了多种形态的UI可供选择

    typedef NS_ENUM(NSInteger, UIDatePickerMode) {
        UIDatePickerModeTime,           // Displays hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. 6 | 53 | PM)
        UIDatePickerModeDate,           // Displays month, day, and year depending on the locale setting (e.g. November | 15 | 2007)
        UIDatePickerModeDateAndTime,    // Displays date, hour, minute, and optionally AM/PM designation depending on the locale setting (e.g. Wed Nov 15 | 6 | 53 | PM)
        UIDatePickerModeCountDownTimer, // Displays hour and minute (e.g. 1 | 53)
    } __TVOS_PROHIBITED;
    

    对应样式如下

    1.1 UIDatePickerModeTime
    UIDatePickerModeTime
    1.2 UIDatePickerModeDate
    UIDatePickerModeDate
    1.3 UIDatePickerModeDateAndTime
    UIDatePickerModeDateAndTime
    1.4 UIDatePickerModeCountDownTimer
    UIDatePickerModeCountDownTimer

    2. Cron表达式

    cron表达式就是对时间数据的一种处理,可以和后台商量好都使用这个方式去解析时间数据,举个例子:8 27 22 1 3 ? 2019,代表时间2019年3月1日22点27点8秒,具体代码如下
    获取当前时间值(秒为单位)

    NSDate* nowDate = [NSDate date];
    NSTimeZone* zone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];
    NSTimeInterval time = [zone secondsFromGMTForDate:nowDate];// 以秒为单位返回当前时间与系统格林尼治时间的差
    int newTime = ([nowDate timeIntervalSince1970]+time);
    int nowhour = newTime / 3600;
    int nowminite = newTime / 60 % 60;
    int newsecond = newTime % 60;
    nowhour %= 24;
    newTime = 3600*nowhour+60*nowminite+newsecond;
    

    在将newTime传入下面参数,即可获得当前时间的cron表达式

    +(NSString*)cronStringWithtime:(long long)time
                              week:(NSInteger)week
                           weekday:(NSInteger)weekday
    {
        NSString* cron;
        
        long hour = time / 3600;
        long min = time / 60 % 60;
        long second = time % 60;
        
        //执行一次 应该设置 日月年, tbd.
        NSString* weekDesp=@"";
        if(week == 0){ //once
            weekDesp = @"?";
            NSDate* date = [NSDate date];
            NSCalendar * calendar = [NSCalendar currentCalendar]; // 指定日历的算法
            NSDateComponents *comps = [calendar components:kCFCalendarUnitSecond|NSCalendarUnitMinute|NSCalendarUnitHour|NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:date];
            long long currentTime=comps.hour*60*60+comps.minute*60+comps.second;
            if (currentTime>=time) {
                NSDate *tomorrowDate = [NSDate dateWithTimeIntervalSinceNow:(24*60*60)];
                comps=[calendar components:kCFCalendarUnitSecond|NSCalendarUnitMinute|NSCalendarUnitHour|NSCalendarUnitDay|NSCalendarUnitMonth|NSCalendarUnitYear fromDate:tomorrowDate];
            }
            cron = [NSString stringWithFormat:@"%ld %ld %ld %ld %ld %@ %ld",second,min,hour,comps.day,comps.month,weekDesp,comps.year];
            return cron;
        }else if(week == 127){ //every day
            weekDesp = @"*";
        }else{
            NSMutableString *weekString = [NSMutableString new];
            NSInteger weekIndex = 0;
            while (week) {
                if (week & 0x1) {
                    if (0 != weekString.length) {
                        [weekString appendString:@","];
                    }
                    [weekString appendFormat:@"%ld", weekIndex+1]; //cron from 1-7
                }
                week >>= 1;
                ++weekIndex;
            }
            weekDesp = weekString;
        }
        
        //Seconds Minutes Hours Day-of-Month Month Day-of-Week Year
        cron = [NSString stringWithFormat:@"%ld %ld %ld ? * %@ *",second,min,hour,weekDesp];   //cron = @"0 0 8 ? * 2,3,4,5,6 *";
        
        return cron;
    }
    

    倒计时的本质也就是对比时间差,所以小伙伴要根据实际项目中的数据做处理,cron表达式只是其中一种,下面介绍另一种时间数据处理

    #pragma mark - 定时器Hander
    - (void)timerHander {
        NSString *redEffectiveTime = @"2019-3-1 16:00:00";
        
        NSDate *nowDate = [NSDate date];
        NSDateFormatter *dateFomatter = [[NSDateFormatter alloc] init];
        dateFomatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
        // 截止时间字符串格式
        NSString *expireDateStr = redEffectiveTime;
        // 当前时间字符串格式
        NSString *nowDateStr = [dateFomatter stringFromDate:nowDate];
        // 截止时间data格式
        NSDate *expireDate = [dateFomatter dateFromString:expireDateStr];
        // 当前时间data格式
        nowDate = [dateFomatter dateFromString:nowDateStr];
        // 当前日历
        NSCalendar *calendar = [NSCalendar currentCalendar];
        // 需要对比的时间数据
        NSCalendarUnit unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
        // 对比时间差
        NSDateComponents *dateCom = [calendar components:unit fromDate:nowDate toDate:expireDate options:0];
        
        NSString *day = [NSString stringWithFormat:@"%d",(int)dateCom.day];
        NSString *hours = [NSString stringWithFormat:@"%02d",(int)dateCom.hour];
        NSString *minutes = [NSString stringWithFormat:@"%02d",(int)dateCom.minute];
        NSString *seconds = [NSString stringWithFormat:@"%02d",(int)dateCom.second];
        
        NSString *time = @"";
        if ([day intValue]>0) {
            time = [NSString stringWithFormat:@"剩余时间:%@天%@:%@:%@",day,hours,minutes,seconds];
        }else{
            time = [NSString stringWithFormat:@"剩余时间:%@:%@:%@",hours,minutes,seconds];
        }
    }
    

    3. dispatch_source_t

    定时器选择了GCD的dispatch_source_t,没有选择平时最常用的定时器NSTime,因为Timer有很多缺点,如

    ①循环引用导致内存泄漏
    ②因为受runloop影响定时可能不准确
    ③代码繁多

    使用dispatch_source_t有效的避免上面问题

    ①将self作为传入方法,避免了循环引用
    ②底层语言实现,不依赖runloop不会出现线程拥堵导致的定时不准确问题
    ③block块代码看起来更简洁,方便管理

    具体代码如下

    #pragma mark 开启定时器
    - (void)createGCDTimer:(NSString *)schedule{
        //获得服务器传输过来的cron时间
        NSString* desc = [HYCronTimerUtils displaySecondStringWithcronString:schedule]; //得到12:27
        NSArray *tempArr = [desc componentsSeparatedByString:@":"];  //得到12 和 27
        NSString* hour = tempArr[0];
        NSString* min = tempArr[1];
        NSString* second = tempArr[2];
        int mins = [hour intValue]*3600+[min intValue]*60+[second intValue];
        
        //得到当前时间
        NSDate* nowDate = [NSDate date];
        NSTimeZone* zone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];
        NSTimeInterval time = [zone secondsFromGMTForDate:nowDate];// 以秒为单位返回当前时间与系统格林尼治时间的差
        int newTime = ([nowDate timeIntervalSince1970]+time);
        int nowhour = newTime / 3600;
        int nowminite = newTime / 60 % 60;
        int newsecond = newTime % 60;
        nowhour %= 24;
        newTime = 3600*nowhour+60*nowminite+newsecond;
        
        //得到倒计时时间
        __block int timeout = mins-newTime; 
    
        //定时器的创建
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
        dispatch_source_set_timer(self.timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
        dispatch_source_set_event_handler(self.timer, ^{
            if(timeout <= 0){ //倒计时结束,关闭
                dispatch_source_cancel(self.timer);
                dispatch_async(dispatch_get_main_queue(), ^{
                    //设置界面的按钮显示 根据自己需求设置
                    NSLog(@"倒计时结束");
                });
            }else{
                int hour = timeout / 3600;
                int minute = timeout / 60 % 60;
                int second = timeout % 60;
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    //设置界面的按钮显示 根据自己需求设置
                    self.timerLabel.text = [NSString stringWithFormat:@"%02d:%02d:%02d",hour,minute,second];
                });
                timeout -= 1;
            }
        });
        dispatch_resume(self.timer);
    }
    

    定时器肯定需要注意释放问题,需要在项目中合适的时机释放该定时器

    #pragma mark 生命周期
    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        dispatch_source_cancel(_timer);   //关闭定时器
    }
    
    - (void)dealloc
    {
        NSLog(@"释放了我就放心了!!!");   //可以在dealloc看一下有没有log的打印
    }
    

    完整定时功能Demo下载地址:
    https://github.com/gaoyuGood/UIDatePicker-Cron-dispatch_source_t

    相关文章

      网友评论

        本文标题:iOS开发-倒计时功能

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