1、思路一(失败)

在设置好cell 里的内容之后在每个cell 返回时调用定时器事件,更新cell 内容,然后刷新整个表格。

- (void)didadida:(UITableViewCell *)cell indexPath:(NSIndexPath*)indexPath{
__weak typeof(self) weakSelf = self;
if (@available(iOS 10.0, *)) {
self.timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
cell.textLabel.text = weakSelf.arrM[indexPath.row];
}];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
} else {
// Fallback on earlier versions
}
}

简单粗暴不奏效。

1、一秒刷新一次整个表格是不现实的,如果定时器更新频率为一秒60次呢?显然不合理

2、需要管理Timer,Timer的销毁时机不确定。

2、利用GCD的 dispatch_source_create 一个 DISPATCH_SOURCE_TYPE_TIMER

1、假如后台返回的活动结束时间在我们已经格式化为时间戳放在一个数组 self.arrM 里。

2、需要一个格式化时间的方法

3、(重点)每秒更新时间的方法

4、记得销毁定时器

@interface XXX()<UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong) dispatch_source_t timer;
@property (nonatomic, strong) NSMutableArray *timestampArr; @end //1、viewDidLoad里 self.timestampArr 三个活动结束的时间格式化为时间戳后放在数组里
NSArray *dateTimeArr = @[@"2019-04-15 17:30:00", @"2019-04-15 18:30:00", @"2019-04-15 20:30:00"];
[dateTimeArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSInteger tempTimestamp = [NSString timeToTimestamp:(NSString *)obj andFormatter:@"YYYY-MM-dd HH:mm:00"];
[self.timestampArr addObject:@(tempTimestamp)];
}]; //把活动结束时间戳和tableView 传递给目标更新函数,正常逻辑是在请求服务器返回时间列表后更新这些数据
[self countPerSecond:self.tableView dataList:self.timestampArr]; //返回cell函数里
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIDT];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIDT];
} cell.textLabel.text = [self caculateNowTime:[NSString getNowTimestamp] activeStartTime:[self.timestampArr[indexPath.row] longLongValue]];
cell.tag = indexPath.row;
cell.textLabel.textColor = [UIColor blackColor];
NSLog(@"cell text: %@",self.timestampArr[indexPath.row]);
return cell;
} /**
2 计算倒计时时间 @param nowTime 现在的时间
@param startTime 活动开始时间
@return 根据距离活动时间的长短格式化时间显示
*/
- (NSString *)caculateNowTime:(NSInteger)nowTime activeStartTime:(NSInteger)startTime {
NSTimeInterval timeInterval = nowTime - startTime;
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*360 - hours*3600 - minutes*60;
NSString *dayStr, *hoursStr, *minutesStr, *secondStr;
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) {
secondStr = [NSString stringWithFormat:@"0%d",seconds];
}else {
secondStr = [NSString stringWithFormat:@"%d",seconds];
} if (days) {
return [NSString stringWithFormat:@"%@天 %@小时 %@分 %@秒",dayStr, hoursStr, minutesStr, secondStr];
}
return [NSString stringWithFormat:@"%@小时 %@分 %@秒",hoursStr,minutesStr,secondStr];
} //3每秒更新时间
- (void)countPerSecond:(UITableView *)tableView dataList:(NSArray *)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;
for (UITableViewCell *cell in cells) {
NSDate *phoneTime = [NSDate date];
NSInteger phoneTimeInteger = [phoneTime timeIntervalSince1970];
NSString *tempEndTime = dataList[cell.tag];
NSInteger endTime = tempEndTime.longLongValue;//
cell.textLabel.text = [self caculateNowTime:endTime activeStartTime:phoneTimeInteger]; if ([cell.textLabel.text isEqualToString:@"活动已结束!"]) {
cell.textLabel.textColor = [UIColor redColor];
}else {
cell.textLabel.textColor = [UIColor orangeColor];
}
}
});
});
dispatch_resume(_timer);
}
}
//4
- (void)destoryTimer {
if (_timer) {
dispatch_source_cancel(_timer);
_timer = nil;
}
}

这样的思路是可以走通的,但是还有许多需要优化的地方,如由于用的是时间差,计时数据可能是负的。

还有,如果用户改变了,自己手机的时间。我们再获取手机时间和结束时间做差也是不准确的,恶意用户可能用这个漏洞。

针对用户改变手机时间,我们可以在应用从后台进入前台时从新从后台获取一次活动结束时间。

时间,时间戳的转化函数

/**
获取当前时间戳 @return 当前时间戳
*/
+ (NSInteger)getNowTimestamp {
//设置时间格式
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
[formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"]; //设置时区
NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
[formatter setTimeZone:timeZone];
NSDate *dateNow = [NSDate date]; NSLog(@"设备当前的时间:%@",[formatter stringFromDate:dateNow]); //时间转时间戳
NSInteger timeStamp = [[NSNumber numberWithDouble:[dateNow timeIntervalSince1970]] integerValue];
NSLog(@"设备当前时间戳:%ld",(long)timeStamp);
return timeStamp;
} /**
时间转时间戳 @param formatTime 格式化的时间字符串
@param format 时间格式
@return 时间戳
*/
+ (NSInteger)timeToTimestamp:(NSString *)formatTime andFormatter:(NSString *)format {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
[formatter setDateFormat:format]; NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
[formatter setTimeZone:timeZone]; NSDate *date = [formatter dateFromString:formatTime]; //时间转时间戳的方法
NSInteger timestamp = [[NSNumber numberWithDouble:[date timeIntervalSince1970]] integerValue];
NSLog(@"将某个时间转化成时间戳 : %ld",(long)timestamp);
return timestamp;
} /**
时间戳转字符串 @param timestamp 时间戳
@param format 输出的时间格式
@return 时间
*/
+ (NSString *)timeStampToTime:(NSInteger)timestamp andFormatter:(NSString *)format {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
[formatter setDateFormat:format]; NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
[formatter setTimeZone:timeZone];
NSDate *dateTime = [NSDate dateWithTimeIntervalSince1970:timestamp]; NSLog(@"time : %@", dateTime); NSString *timeStr = [formatter stringFromDate:dateTime]; return timeStr;
}

参考 https://www.jianshu.com/p/85909aabf058

利用GCD 中的 dispatch_source_timer 给tableViewCell添加动态刷新的计时/倒计时功能的更多相关文章

  1. 两种利用GCD实现分步获取结果的方式和SDWebImage缓存机制的验证

    前段时间写界面,因为数据的请求分成了两部分,所以用到了多线程,实现数据的分步请求,然后自己写了一个Demo,用两种方式实现分步获取内容,其中也包含了验证SDWebImage这个库的缓存机制,在这里给大 ...

  2. 如何利用excel中的数据源制作数据地图

    关于这个问题,制作数据地图的方法已不新奇,总体来说有这么几类方案: 一类方案:直接在excel里制作 优势:个人小数据量应用较为方便简单 缺点:需要熟悉VBA,且更强大的功能对VBA水平要求较高 1. ...

  3. iOS 关于GCD中的队列

    GCD中队列分类及获得方式 1.串行队列  dispatch_queue_t queue = dispatch_queue_create("队列名", DISPATCH_QUEUE ...

  4. 利用Python中的mock库对Python代码进行模拟测试

    这篇文章主要介绍了利用Python中的mock库对Python代码进行模拟测试,mock库自从Python3.3依赖成为了Python的内置库,本文也等于介绍了该库的用法,需要的朋友可以参考下     ...

  5. iOS:对GCD中 同步、异步、并行、串行的见解

    1.GCD-同步执行多线程时          GCD中不管向什么类型的队列加同步任务,实际上都会加到当前线程中(一般为主线程). 2.GCD-异步执行多线程时          GCD中不管向什么类 ...

  6. 在Spring-boot中,为@Value注解添加从数据库读取properties支持

    一般我们会把常用的属性放在工程的classpath文件夹中,以property,yaml或json的格式进行文件存储,便于Spring-boot在初始化时获取. @Value则是Spring一个非常有 ...

  7. QTabWidget 中 关于Tab 关闭和添加的基本教程!

    QTabWidget是PyQt5 中使用较为广泛的容器之一,经常会在日常使用的软件中用到它:QTabwidget是由几个标签组成,每个标签可以当作一个界面,下面就是应用Qtabwidget的一个简单例 ...

  8. 数据库中老师学生家长表添加自动同意好友自动(AgreeAddingFriend ),默认为True

    数据库中老师学生家长表添加自动同意好友自动(AgreeAddingFriend ),默认为True alter table Sys_User add AgreeAddingFriend bit alt ...

  9. asp.net中的ListBox控件添加双击事件

    问题:在Aspx页里的ListBox A中添加双击事件,将选中项添加到另一个ListBox B中,双击ListBox B中的选中项,删除当前选中项 页面: <asp:ListBox ID=&qu ...

随机推荐

  1. Distance on the tree(数剖 + 主席树)

    题目链接:https://nanti.jisuanke.com/t/38229 题目大意:给你n个点,n-1条边,然后是m次询问,每一次询问给你u,v,w然后问你从u -> v 的路径上有多少边 ...

  2. django上下文处理器的基本使用

    1.定义一个方法 2.在django里面的settings.py里面修改配置文件 3.最后在模板里面调用 操做步骤如下: 这是在settings.py里面配置的文件   在模板里面调用上下文处理器

  3. (五)Java工程化--Jenkins

    Jenkins简介 Jenkins 是一种用Java语言实现的持续集成工具,Jenkins是一个平台, 在此基础上实现下面两个目的. CI 持续集成(Continous Integration) CD ...

  4. Ubuntu 16.04下安装MySQL及远程连接

    最近因为要研究一个关于MySQL的漏洞,所以需要MySQL的环境,就用了近一个小时的时间搭建了一个,期间出了点问题,故记录于此. 1.首先是安装,在命令窗口中输入下面三条命令即可. sudo apt- ...

  5. python进制转化函数,10进制字符串互转,16进制字符串互转

    来了老弟,emmmmm,今天想到平时经常用到编码转化,把字符串转化为16进制绕过等等的,今天想着用python写个玩,查询了一些资料,看了些bolg 上面的两个函数是将二进制流转化为16进制,data ...

  6. C++设计模式——迭代器模式

    前言 最近非常感伤,总是怀念大学的日子,做梦的时候也常常梦到.梦到大学在电脑前傻傻的敲着键盘,写着代码,对付着数据结构与算法的作业:建立一个链表,遍历链表,打印链表.现在把那个时候声明的链表的头文件拿 ...

  7. Kafka简单使用

    前言 这几天在写 shell 脚本,学到不少,但是没啥心得之类的,有空可以写个总结(但是大概率不会发表) 现在不算很忙,想再学一点 消息队列相关知识 目前比较出名的也就 RabbitMQ 和 Kafk ...

  8. Monkey自动化脚本(一)

    1.Monkey简介 Monkey-猴子,通过Monkey程序模拟用户触摸屏幕.滑动Trackball. 按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常,主要用于Android ...

  9. Flask开发微电影网站(六)

    1. 后台管理登录功能实现 1.1 后台管理页面登录表单LoginForm 在app的admin目录下创建forms.py文件,用来保存admin蓝图中需要使用到的表单 from flask_wtf ...

  10. 十三.iptabled配置

    期中集群架构-第十三章-iptables防火墙网路安全实践配置========================================= 01:iptables防火墙网路安全前言介绍 学好ip ...