cell (iOS表格) - 簡單實現一個定時器管理600個倒數計時 - 2.程式碼介紹篇

weixin_34124651發表於2016-10-22

傳送門:
效果展示http://www.jianshu.com/p/3c49b44e45b4
如何使用: http://www.jianshu.com/p/5b4e0286658a
下載地址: https://github.com/zhYes/YSTimeCountDown

1914107-0cf3c8b4b666ea8d.jpg
Facai - 公司萌寵

//2018年03月20日09:48:01更新:
//2018年03月20日09:48:01更新:
//2018年03月20日09:48:01更新:
~
有朋友反映出現了倒數計時一萬多天的情況,經過幾次除錯,發現我這裡有一個獲取當前時間時間戳的介面 用來校準伺服器時間和手機當前時間的差值
當這個介面不好用 獲取不到的時候就是這個樣子了 建議讓後臺自己做個介面 來替換 YSCountDown.m裡面的@"http://api.k780.com:88/?app=life.time&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json"這個介面就好了,即:

1914107-cf935766150d219d.jpg
介面位置.jpg

1914107-1fb361d12c5f1264.gif
QQ20180320-095232.gif

注意:1.時間戳單位這裡是秒.2.自己介面取時間戳的key替換.
1914107-19e61d287d49ae4f
最新demo截圖

2018年03月20日@end
2018年03月20日@end
2018年03月20日@end
~

正文:

需求是這樣的,對UITableView中的每個Cell 加入一個倒數計時器的顯示,如下圖:

1914107-e4c6ba54b19bbc33.gif

一、倒數計時的實現

首先計時器這塊,我第一個會想到是用NSTimer定時器,還是用GCD定時器,或者說CADisplayLink定時呢。

經過粗略的比較這裡採用GCD定時器

NSTimer是必須要在run loop已經啟用的情況下使用的否則無效。而只有主執行緒是預設啟動runLoop的。
我們不能保證自己寫的方法不會被人在非同步的情況下呼叫到,所以有時使用NSTimer不是很保險的。
同時 NSTime 的坑比較多,迴圈應用和 RunLoop 那塊的坑都可以開專題啦。
而CADisplayLink相對來說比較適合做介面的不停重繪。
NStimer是在RunLoop的基礎上執行的,然而RunLoop是在GCD基礎上實現的,所以說GCD可算是更加高階。

本著封裝的思想,工具模型.h這裡我們提供一個供外界呼叫的方法 ?

//需要傳入 1.顯示倒數計時的tableView | 2.結束時刻的時間戳字串陣列 
- (void)countDownWithPER_SEC: (UITableView*)tableView :(NSArray*)dataList;

常規程式碼: 頻繁會被呼叫的計算顯示時間方法?

// 傳入結束時間 | 計算與當前時間的差值
- (NSString *)getNowTimeWithString:(NSInteger )aTimeString :(NSInteger)startTime{
    
    NSTimeInterval timeInterval = aTimeString - startTime;
    //    NSLog(@"%f",timeInterval);
    int days = (int)(timeInterval/(3600*24));
    int hours = (int)((timeInterval-days*24*3600)/3600);
    int minutes = (int)(timeInterval-days*24*3600-hours*3600)/60;
    int seconds = timeInterval-days*24*3600-hours*3600-minutes*60;
    
    NSString *dayStr;NSString *hoursStr;NSString *minutesStr;NSString *secondsStr;
    //天
    dayStr = [NSString stringWithFormat:@"%d",days];
    //小時
    hoursStr = [NSString stringWithFormat:@"%d",hours];
    //分鐘
    if(minutes<10)
        minutesStr = [NSString stringWithFormat:@"0%d",minutes];
    else
        minutesStr = [NSString stringWithFormat:@"%d",minutes];
    //秒
    if(seconds < 10)
        secondsStr = [NSString stringWithFormat:@"0%d", seconds];
    else
        secondsStr = [NSString stringWithFormat:@"%d",seconds];
    if (hours<=0&&minutes<=0&&seconds<=0) {
        return @"活動已經結束!";
    }
    if (days) {
        return [NSString stringWithFormat:@"%@天 %@小時 %@分 %@秒", dayStr,hoursStr, minutesStr,secondsStr];
    }
    return [NSString stringWithFormat:@"%@小時 %@分 %@秒",hoursStr , minutesStr,secondsStr];
}
  • 核心程式碼1: 介面獲得真實的伺服器返回時間,並計算與當前系統時間差值! ?
- (void)setupLess {
    
    AFHTTPSessionManager * manager = [AFHTTPSessionManager manager];
    [manager POST:@"http://api.k780.com:88/?app=life.time&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, NSDictionary* responseObject) {
        //                NSLog(@"%@",responseObject);
        NSString * webCurrentTimeStr = responseObject[@"result"][@"timestamp"];
        NSInteger webCurrentTime = webCurrentTimeStr.longLongValue;
        NSDate * date = [NSDate date];
        NSInteger nowInteger = [date timeIntervalSince1970];
        _less = nowInteger - webCurrentTime;
        NSLog(@" --  與伺服器時間的差值 -- %zd",_less);
        
        if (_dataList) {
            [self destoryTimer];
            [self countDownWithPER_SEC:_myTableView :_dataList];
        }
        
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"%@ - 網路錯誤 ! ",error);
    }];
}
  • 核心程式碼2:
  • 利用定時器,每秒改變當前tableView可見cell需要倒數計時時間的顯示
  • 利用[NSDate date]每秒遞增與結束時間做差值,這樣每次被GCD定時器調取的時候所顯示的值差1s,出現我們想要的倒數計時效果
  • 再利用介面獲取的當前伺服器準確時間與手機系統時間做差值,一次性校準我們的倒數計時 ! 而不是頻繁呼叫介面 ?
- (void)countDownWithPER_SEC :(UITableView*)tableView :(NSArray*)dataList {
    
    _myTableView = tableView;
    _dataList = dataList;
    if (_timer==nil) {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        _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, ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                
                NSArray  *cells = tableView.visibleCells; //取出螢幕可見ceLl
                for (UITableViewCell *cell in cells) {
                    
                    NSDate * sjDate = [NSDate date];   //手機時間
                    NSInteger sjInteger = [sjDate timeIntervalSince1970];  // 手機當前時間戳
                    NSString* tempEndTime = dataList[cell.tag];
                    
                    NSInteger endTime = tempEndTime.longLongValue + _less;
                    
                    cell.textLabel.text = [self getNowTimeWithString:endTime :sjInteger];
                    
                    
                    if ([cell.textLabel.text isEqualToString:@"活動已經結束!"]) {
                        cell.textLabel.textColor = [UIColor redColor];
                    }else{
                        cell.textLabel.textColor = [UIColor orangeColor];
                    }
//                    NSLog(@" -------- %@ ----  %@",cell.detailTextLabel.text,cell.textLabel.text);
                }
            });
        });
        dispatch_resume(_timer); // 啟動定時器
    }

}

核心程式碼3: 當別人想通過修改本地時間 影響倒數計時時怎麼辦??

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupLess) name:UIApplicationDidBecomeActiveNotification object:nil];

這裡只需通過通知中心監聽 我們的APP從後臺重新變為前臺UIApplicationDidBecomeActiveNotification並重新計算_less差值 為什麼?
好吧,應該不用解釋

/**
 *  主動銷燬定時器
 */
-(void)destoryTimer{
    if (_timer) {
        dispatch_source_cancel(_timer);
        _timer = nil;
    }
}

最後一段常規程式碼: 銷燬 ?

1914107-f25ebb99f13f806c.gif
改變本地時間依然正確演示

========= 任何其他問題,歡迎留言,願與你一起學習?=====

相關文章