1.简介
- 有时候我们需要在
UITableView
中的cell
做一些类似京东天猫的倒计时抢购的功能,时间到了会发送一个通知提醒用户可以抢购了。 - 其原理无非是通过定时器刷新
UITableView
,这里有两种方式:- 在每一个cell中都添加一个定时器,计算剩余时间,然后实时刷新更改对应
UILable
的文字; - 开启一个定时器,然后遍历数据源计算剩余时间,然后刷新
UITableView
,考虑到内存开销,本文中采用的第二种方式;
- 在每一个cell中都添加一个定时器,计算剩余时间,然后实时刷新更改对应
2.创建定时器
-
创建定时器有3种方法(可参考这里介绍:OC中定时器的三种实现方式):
- NSTimer:虽然简单易用,但是定时不准确;
- CADisplayLink:能保证和屏幕刷新率相同的频率,将特定的内容画到屏幕上,其实时间间隔是和屏幕刷新频率相关联的;
- dispatch_source:也称为GCD定时器,不受 runloop 模式的影响,使用 dispatch_source 能够实现没有延迟的定时器,即可以实现高精度定时,所以本为中采用dispatch_source定时。
-
具体步骤:当获取的
TableView
数据源后,开启定时器计算时间差
//创建GCD定时器
gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//设置定时器
dispatch_source_set_timer(gcdTimer, dispatch_walltime(NULL, 0), 1ull * NSEC_PER_SEC, 0);
//设置定时器任务
dispatch_source_set_event_handler(gcdTimer, ^{
//遍历数据源,计算时间差,并且给对应的cell设置对应的时间差
for (int index = 0; index<weakSelf.dataSource.count; index++) {
UntreatedOrderModel * model = weakSelf.dataSource[index];
model.countdownTime = [weakSelf dateTimeIntervalWithEndTime:model.qdsxTime];
__block typeof(model) blockModel = model;
dispatch_async(dispatch_get_main_queue(), ^{
UntreatedOrderCell * cell = [weakTableV cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];
cell.model = blockModel;
});
}
});
// 启动任务,GCD计时器创建后需要手动启动
dispatch_resume(gcdTimer);
3.计算时间差
- 计算到现在的时间差:
timeIntervalSinceNow
,计算两个时间点之间的时间差:timeIntervalSinceDate:
,计算的时间差是以秒为单位的,所以可以得到对应的天、时、分、秒。
-(NSString *)dateTimeIntervalWithEndTime:(NSString *)endTime{
//设定时间戳样式
NSDateFormatter * dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate * endD = [dateFormatter dateFromString:endTime];
NSTimeInterval value = [endD timeIntervalSinceNow];
int second = (int)value %60;//秒
int minute = (int)value / 60%60;//分
int hour = (int)value / 3600%24;//时
int day = (int)value / (24 *3600);//天
NSString *str;
if (day > 0) {
str = [NSString stringWithFormat:@"%d天%d时%d分%d秒",day,hour,minute,second];
}else if (day==0 && hour > 0) {
str = [NSString stringWithFormat:@"%d时%d分%d秒",hour,minute,second];
}else if (day==0 && hour==0 && minute>0) {
str = [NSString stringWithFormat:@"%d分%d秒",minute,second];
}else if (day==0 && hour==0 && minute==0 && second>0){
str = [NSString stringWithFormat:@"%d秒",second];
}else{
str = @"已超时";
}
return str;
}
注意:
这里为啥调用reloadData
方法刷新UITableView
,而是获取每一个cell
然后给cell
赋值,如果频繁调用reloadData
去刷新UITableView
,会发现有事件冲突,滚动的时候不够流畅,滚动的位置有时候并不是自己想要的,虽然有人说可以设置NSRunLoop
的Mode
为NSRunLoopCommonModes
来防止事件冲突,但我试了并不行;而获取每一个cell
然后给cell
赋值是丝滑流畅的。(当然肯定有方法可以解决,有知道的还烦请不吝赐教)。