SwiftObject-C

干货!Swift/OC一个倒计时.计时器

2017-11-29  本文已影响196人  66b6d3e5fc98

计时器案例场景:短信验证码、商品抢购倒计时……
实现目标:

png

Swift版本

class CD_CountDown {
    fileprivate static let sharedInstance = CD_CountDown()
    fileprivate init() {}
    //提供静态访问方法
    open static var shared: CD_CountDown {
        return self.sharedInstance
    }
    ///时间倒计时标识存储,预防重复创建同一计时线程
    var notifyNames:[String] = []
}
extension CD_CountDown {
    //MARK:--- 时间倒计时 ----------
    ///时间倒计时
    open class func addCountDown(_ notifyName:String, nowTimestamp:TimeInterval, endTimestamp:TimeInterval, second:Double = 0.1) {
        //以notifyName为标识,不必重复创建线程
        if CD_CountDown.shared.notifyNames.contains(notifyName) {return}
        CD_CountDown.shared.notifyNames.append(notifyName)
        //截止日期
        let endDate:Date = Date(timeIntervalSince1970: endTimestamp)
        //当前时间
        let nowDate:Date = Date(timeIntervalSince1970: nowTimestamp)
        //当前时间与系统时间差
        let nowDateDiffer:TimeInterval = nowDate.timeIntervalSinceNow
        //时间间隔 ^ 毫秒
        var aTime:TimeInterval = endDate.timeIntervalSince(Date()) * 10
        var bTime:Int = 999
        
        var saveSecond:Int  = 59
        // 创建一个时间源
        let queue = DispatchQueue(label: notifyName)
        let timer = DispatchSource.makeTimerSource(queue:queue)
        //循环执行,马上开始,间隔为1ms
        timer.schedule(deadline: .now(), repeating: .milliseconds(Int(second*1000)))
        // 设定时间源的触发事件
        timer.setEventHandler(handler: {
            //计算剩余时间
            let gregorian = Calendar(identifier: .gregorian)
            let cmps = gregorian.dateComponents([Calendar.Component.year,Calendar.Component.month,Calendar.Component.day,Calendar.Component.hour,Calendar.Component.minute,Calendar.Component.second], from: Date(timeIntervalSince1970: Date().timeIntervalSince1970 + nowDateDiffer), to: endDate)
            aTime = endDate.timeIntervalSince(Date(timeIntervalSince1970: Date().timeIntervalSince1970 + nowDateDiffer)) * 10
            
            bTime = saveSecond != cmps.second ? 999 : (bTime <= 0 ? 999 : bTime - Int(second*1000))
            saveSecond = cmps.second ?? 59
            
            if aTime <= 0 {
                timer.cancel()
                DispatchQueue.main.async(execute: {
                    //print("通知\(aTime)毫秒")
                    CD_CountDown.shared.notifyNames.remove(at: CD_CountDown.shared.notifyNames.index(of: notifyName)!)
                    NotificationCenter.default.post(name: NSNotification.Name.init(notifyName), object: M_CD_CountDownTime(year: 0, month: 0, day: 0, hour: 0, minute: 0, second: 0, millisecond: 0))
                })
            } else {
                DispatchQueue.main.async(execute: {
                    //print("通知\(aTime)毫秒")
                    NotificationCenter.default.post(name: NSNotification.Name.init(notifyName), object: M_CD_CountDownTime(year: cmps.year ?? 0, month: cmps.month ?? 0, day: cmps.day ?? 0, hour: cmps.hour ?? 0, minute: cmps.minute ?? 0, second: cmps.second ?? 0, millisecond: bTime*10+cTime))
                })
                
            }
        })
        // 启动时间源
        timer.resume()
    }
}
extension CD_CountDown {
    //MARK:--- 验证码秒表倒计时 ----------
    ///验证码秒表倒计时
    open class func addVerifyCode(_ notifyName:String, maxTime: Int = 60) {
        //以notifyName为标识,不必重复创建线程
        if CD_CountDown.shared.notifyNames.contains(notifyName) {return}
        CD_CountDown.shared.notifyNames.append(notifyName)
        let toTime = Date().timeIntervalSince1970 + TimeInterval(maxTime)
        var aTime = maxTime
        let queue = DispatchQueue(label: notifyName)
        // 创建一个时间源
        let timer = DispatchSource.makeTimerSource(queue:queue)
        //循环执行,马上开始,间隔为0.1s,误差允许10微秒
        timer.scheduleRepeating(deadline: .now(), interval: .seconds(1), leeway: .milliseconds(10))
        // 设定时间源的触发事件
        timer.setEventHandler(handler: {
            aTime = Int(toTime - Date().timeIntervalSince1970)
            if aTime <= 0 {
                //timer.suspend()
                timer.cancel()
                
                DispatchQueue.main.async {
                    print("通知\(aTime)")
                    CD_CountDown.shared.notifyNames.remove(at: CD_CountDown.shared.notifyNames.index(of: notifyName)!)
                    NotificationCenter.default.post(name: NSNotification.Name.init(notifyName), object: 0)
                }
            }else{
                DispatchQueue.main.async {
                    print("通知\(aTime)")
                    NotificationCenter.default.post(name: NSNotification.Name.init(notifyName), object: aTime)
                }
            }
        })
        // 启动时间源
        timer.resume()
    }
}
struct M_CD_CountDownTime {
    var year:Int = 0
    var month:Int = 0
    var day:Int = 0
    var hour:Int = 0
    var minute:Int = 0
    var second:Int = 0
    var millisecond:Int = 0
}
//验证码秒表倒计时
CD_CountDown.addVerifyCode("VerificationCodeName1", maxTime: 10)
cd_Notify.rx
            .notification(Notification.Name(rawValue:"VerificationCodeName1"))
            .subscribe(onNext: { [weak self](n) in
                //print("收到通知\(String(describing: n.object))")
                self?.upBtn(self!.btn_1, time: n.object as! Int)
            }).disposed(by:disposeBag)

//日期倒计时
        CD_CountDown.addCountDown("CD_CountDownNotificationName2030", nowTimestamp: Double(Date().timeIntervalSince1970 + 10.1), endTimestamp: 1893427200)
        cd_Notify.rx
            .notification(Notification.Name(rawValue:"CD_CountDownNotificationName2030"))
           .subscribe(onNext: { [weak self](n) in
                //print("收到通知\(String(describing: n.object))")
                guard let m = n.object as? M_CD_CountDownTime else{return}
                self?.lab_time.text = String(format: "%d年%.2d月%.2d日 %.2d:%.2d:%.2d.%.2d", m.year,m.month,m.day,m.hour,m.minute,m.second,m.millisecond)
            }).disposed(by:disposeBag)

OC版本

//.h
@interface CD_CountDown : NSObject
#pragma mark ----- 单例
@property(nonatomic,strong) NSString *name;
+ (CD_CountDown*)shared;
@property(nonatomic,strong) NSMutableArray* notifyNames;

+ (void)addCountDown:(NSString*)notifyName nowTimestamp:(NSTimeInterval)nowTimestamp endTimestamp:(NSTimeInterval)endTimestamp second:(NSTimeInterval)second;
+ (void)addVerifyCode:(NSString*)notifyName maxTime:(NSInteger)maxTime;
@end

//.m
static CD_CountDown* shared = nil;
@implementation CD_CountDown
#pragma mark ----- 单例 -----
+ (CD_CountDown*) shared
{
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        if (shared == nil) {
            shared = [[self alloc] init];
        }
        
    });
    return shared;
}
/**
 覆盖该方法主要确保当用户通过[[Singleton alloc] init]创建对象时对象的唯一性,alloc方法会调用该方法,只不过zone参数默认为nil,因该类覆盖了allocWithZone方法,所以只能通过其父类分配内存,即[super allocWithZone:zone]
 */
+(id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        if(shared == nil){
            shared = [super allocWithZone:zone];
        }
    });
    return shared;
}
//自定义初始化方法,本例中只有name这一属性
- (instancetype)init
{
    self = [super init];
    if(self){
        self.name = @"CD_CountDown";
        self.notifyNames = [NSMutableArray array];
    }
    return self;
}
//覆盖该方法主要确保当用户通过copy方法产生对象时对象的唯一性
- (id)copy
{
    return self;
}
//覆盖该方法主要确保当用户通过mutableCopy方法产生对象时对象的唯一性
- (id)mutableCopy
{
    return self;
}
//自定义描述信息,用于log详细打印
- (NSString *)description
{
    return [NSString stringWithFormat:@"memeory address:%p,property name:%@",self,self.name];
}



#pragma mark ----- 时间倒计时 -----
+ (void)addCountDown:(NSString*)notifyName nowTimestamp:(NSTimeInterval)nowTimestamp endTimestamp:(NSTimeInterval)endTimestamp second:(NSTimeInterval)second{
    //以notifyName为标识,不必重复创建线程
    if ([[[CD_CountDown shared] notifyNames] containsObject:notifyName]) {
        return;
    };
    [[[CD_CountDown shared] notifyNames] addObject:notifyName];
    //[[NSProcessInfo processInfo] systemUptime];开机到现在的时间
    //创建一个队列
    const char * lab = [[NSString stringWithFormat:@"%@.queue",notifyName] UTF8String];
    dispatch_queue_t queue = dispatch_queue_create(lab, DISPATCH_QUEUE_CONCURRENT);
    //创建一个timer放到队列里面
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //设置首次执行时间,时间间隔,精确度
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, second*NSEC_PER_SEC, 0.1*NSEC_PER_SEC);
    
    //截止日期
    NSDate* endDate = [NSDate dateWithTimeIntervalSince1970:endTimestamp];
    //当前时间
    NSDate* nowDate = [NSDate dateWithTimeIntervalSince1970:nowTimestamp];
    //当前时间与系统时间差
    NSTimeInterval nowDateDiffer = nowDate.timeIntervalSinceNow;
    
    //时间间隔 ^ 毫秒
    __block NSTimeInterval aTime = nowDateDiffer * 10;
    __block NSTimeInterval bTime = 999;
    __block NSInteger saveSecond = 59;
    //设置timer执行事件
    dispatch_source_set_event_handler(timer, ^{
        
        //计算剩余时间
        NSDate* fromDate = [NSDate dateWithTimeIntervalSince1970:[NSDate date].timeIntervalSince1970 + nowDateDiffer];
        NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier: NSCalendarIdentifierGregorian];
        NSDateComponents * cmps = [gregorian components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute|NSCalendarUnitSecond
                                               fromDate:fromDate toDate:endDate options:0];
        aTime = [endDate timeIntervalSinceDate:fromDate] * 10;
        
        //时间差
        bTime = saveSecond != cmps.second ? 999 : (bTime <= 0 ? 999 : bTime - Int(second*1000))
        saveSecond = cmps.second;
        
        if (aTime <= 0) {
            //倒计时结束,关闭
            dispatch_source_cancel(timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [[[CD_CountDown shared] notifyNames] removeObject:notifyName];
                M_CD_CountDown* mo = [[M_CD_CountDown alloc] init];
                mo.year = 0;
                mo.month = 0;
                mo.day = 0;
                mo.hour = 0;
                mo.minute = 0;
                mo.second = 0;
                mo.msecond = 0;
                [[NSNotificationCenter defaultCenter] postNotificationName:notifyName object:mo];
            });
        }else{
            dispatch_async(dispatch_get_main_queue(), ^{
                //NSLog(@"发送通知%ld",(long)bTime);
                M_CD_CountDown* mo = [[M_CD_CountDown alloc] init];
                mo.year = cmps.year;
                mo.month = cmps.month;
                mo.day = cmps.day;
                mo.hour = cmps.hour;
                mo.minute = cmps.minute;
                mo.second = cmps.second;
                mo.msecond = bTime*10 + cTime;
                [[NSNotificationCenter defaultCenter] postNotificationName:notifyName object:mo];
            });
        }
    });
    dispatch_resume(timer);
    
    
}

#pragma mark ----- 添加验证码计时队列 -----
+ (void)addVerifyCode:(NSString*)notifyName maxTime:(NSInteger)maxTime{
    //以notifyName为标识,不必重复创建线程
    if ([[[CD_CountDown shared] notifyNames] containsObject:notifyName]) {
        return;
    };
    [[[CD_CountDown shared] notifyNames] addObject:notifyName];
    /*
     使用 系统时间 toTime - [NSDate date].timeIntervalSince1970
     0即使程序挂入后台依然能正确倒计时
     1,获取当前系统时间,在此基础上加上 maxTime,作为结束时间。
     2,没间隔一秒获取当前系统时间,结束时间 - 当前系统时间 = 秒数。
     */
    __block NSInteger toTime = [NSDate date].timeIntervalSince1970 + maxTime;
    __block NSInteger aTime = maxTime;
    //创建一个队列
    const char * lab = [[NSString stringWithFormat:@"%@.queue",notifyName] UTF8String];
    //dispatch_queue_create(lab, DISPATCH_QUEUE_CONCURRENT);
    //dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t queue = dispatch_queue_create(lab, DISPATCH_QUEUE_CONCURRENT);
    //创建一个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.1*NSEC_PER_SEC);
    //设置timer执行事件
    dispatch_source_set_event_handler(timer, ^{
        //时间差
        aTime =  toTime - [NSDate date].timeIntervalSince1970;
        if (aTime <= 0) {
            //倒计时结束,关闭
            dispatch_source_cancel(timer);
            dispatch_async(dispatch_get_main_queue(), ^{
                [[[CD_CountDown shared] notifyNames] removeObject:notifyName];
                [[NSNotificationCenter defaultCenter] postNotificationName:notifyName object:@"0"];
            });
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                //NSLog(@"发送通知%ld",(long)bTime);
                [[NSNotificationCenter defaultCenter] postNotificationName:notifyName object:[NSString stringWithFormat:@"%ld",(long)aTime]];
            });
        }
    });
    dispatch_resume(timer);
}

@end

@interface M_CD_CountDown : NSObject
@property(nonatomic,assign) NSInteger year;
@property(nonatomic,assign) NSInteger month;
@property(nonatomic,assign) NSInteger day;
@property(nonatomic,assign) NSInteger hour;
@property(nonatomic,assign) NSInteger minute;
@property(nonatomic,assign) NSInteger second;
@property(nonatomic,assign) NSInteger msecond;
@end

//验证码秒表倒计时
[CD_CountDown addVerifyCode:@"VerificationCodeName1" maxTime:10];
[[[[CD_Const cd_Notify] rac_addObserverForName:@"VerificationCodeName1" object:nil] takeUntil:self.rac_willDeallocSignal] subscribeNext:^(NSNotification * x) {
        
        [weakSelf updataBtn:weakSelf.btn_1 time:x.object];
    }];
//日期倒计时
[CD_CountDown addCountDown:@"CD_CountDownName2030" nowTimestamp:[NSDate date].timeIntervalSince1970 endTimestamp:1893427200 second:0.1];
    
    [[[[CD_Const cd_Notify] rac_addObserverForName:@"CD_CountDownName2030" object:nil] takeUntil:self.rac_willDeallocSignal] subscribeNext:^(NSNotification * x) {
        M_CD_CountDown* mo = x.object;
        weakSelf.lab_Time.text = [NSString stringWithFormat:@"%.2ld年%.2ld月%.2ld日  %.2ld:%.2ld:%.2ld.%.2ld",mo.year,mo.month,mo.day,mo.hour,mo.minute,mo.second,mo.msecond];
    }];
上一篇下一篇

猜你喜欢

热点阅读