美文网首页iOS开发技能iosUI
iOS 在cell中使用倒计时的处理方法(新)

iOS 在cell中使用倒计时的处理方法(新)

作者: 大头herob | 来源:发表于2017-06-14 14:32 被阅读2456次

一.前言

之前的文章iOS 在cell中使用倒计时的处理方法得到大量的支持, 在这先感谢大家的支持. 但是也收到不少人的回复表示不会用, 需要一一解答, 由于之前写的时候没有使用Markdown编辑, 所以现在没法更新之前的文章, 重新写一份清晰的文章

需求: 每条Cell中显示倒计时, 并随时间进行倒数
语言: Objective-C & Swift
系统: iOS
Github地址: OYCountDownManager v2.0
OYCountDownManager-Swift v2.0

二.原理分析

原理分析图.png
单个列表倒计时.gif

v2.0新增

* 多个列表倒计时
* 多个页面倒计时
* 分页列表倒计时
* 后台模式倒计时
多个列表倒计时.gif 多个页面倒计时.gif 分页列表倒计时.gif

三.使用方法

1.1 第一种方法: 使用cocoapods自动安装

pod 'OYCountDownManager'

1.2 第二种方法

下载示例Demo, 把里面的OYCountDownManager文件夹拖到你的项目中

2. 在第一次使用的地方调用[kCountDownManager start]

- (void)viewDidLoad {
    [super viewDidLoad];

    // 启动倒计时管理
    [kCountDownManager start];
}

3. 在Cell初始化中监听通知 kCountDownNotification

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        // 监听通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(countDownNotification) name:kCountDownNotification object:nil];
    }
    return self;
}

4. 在cell设置通知回调, 取得时间差, 根据时间差进行处理

- (void)countDownNotification {
    /// 计算倒计时
    NSInteger countDown = [self.model.count integerValue] - kCountDownManager.timeInterval;
    if (countDown <= 0) {
          // 倒计时结束时回调
          xxxx(使用代理或block)
    }return;
    /// 重新赋值
    self.timeLabel.text = [NSString stringWithFormat:@"倒计时%02zd:%02zd:%02zd", countDown/3600,       (countDown/60)%60, countDown%60];
}

5. 当刷新数据时,调用reload方法

- (void)reloadData {
    // 网络加载数据

    // 调用[kCountDownManager reload]
    [kCountDownManager reload];
    // 刷新
    [self.tableView reloadData];
}

6. 当不需要倒计时时, 废除定时器

[kCountDownManager invalidate];

四.高级使用(多列表.多页面.分页列表)

增加identifier:标识符, 一个identifier支持一个倒计时源, 有一个单独的时间差

/** 添加倒计时源 */
- (void)addSourceWithIdentifier:(NSString *)identifier;

/** 获取时间差 */
- (NSInteger)timeIntervalWithIdentifier:(NSString *)identifier;

/** 刷新倒计时源 */
- (void)reloadSourceWithIdentifier:(NSString *)identifier;

/** 刷新所有倒计时源 */
- (void)reloadAllSource;

/** 清除倒计时源 */
- (void)removeSourceWithIdentifier:(NSString *)identifier;

/** 清除所有倒计时源 */
- (void)removeAllSource;

以一个页面有两个独立的列表为例

1.定义identifier(常量)

NSString *const OYMultipleTableSource1 = @"OYMultipleTableSource1";
NSString *const OYMultipleTableSource2 = @"OYMultipleTableSource2";

2.增加倒计时源

// 增加倒计时源 
[kCountDownManager addSourceWithIdentifier:OYMultipleTableSource1];
[kCountDownManager addSourceWithIdentifier:OYMultipleTableSource2];

3.在cell通知回调中, 通过identifier取得时间差, 根据时间差进行处理

- (void)countDownNotification {
    /// 判断是否需要倒计时 -- 可能有的cell不需要倒计时,根据真实需求来进行判断
    if (0) {
        return;
    }
    /// 计算倒计时
    OYModel *model = self.model;
    /// 根据identifier取得时间差, 以OYMultipleTableSource1为例
    NSInteger timeInterval = timeInterval = [kCountDownManager timeIntervalWithIdentifier: OYMultipleTableSource1];
    }
    NSInteger countDown = model.count - timeInterval;
    /// 当倒计时到了进行回调
    if (countDown <= 0) {
        self.detailTextLabel.text = @"活动开始";
        // 倒计时结束时回调
          xxxx(使用代理或block)
        return;
    }
    /// 重新赋值
    self.detailTextLabel.text = [NSString stringWithFormat:@"倒计时%02zd:%02zd:%02zd", countDown/3600, (countDown/60)%60, countDown%60];
}

4. 当刷新数据时,调用reloadSourceWithIdentifier:刷新时间差

- (void)reloadData {
    // 网络加载数据

   // 调用reloadSourceWithIdentifier:刷新时间差
   [kCountDownManager reloadSourceWithIdentifier:OYMultiplePageSource1];
    // 刷新
    [self.tableView reloadData];
}

5. 当页面销毁, 移除倒计时源, 或者不需要定时器, 废除定时器

// 移除所有倒计时源
[kCountDownManager removeAllSource];
// 废除定时器
[kCountDownManager invalidate];

五.注意事项

误差分析

  • NSTimer可以精确到50-100毫秒,不是绝对准确的,所以使用时间累加的方法时间久了有可能成为时间误差的来源
  • 为秒为单位触发定时器, 当reloadData后, 定时器也许刚好到达触发点, 时间差+1, 数据刚reload完就马上-1秒
  • 后台模式是以进入后台的本地时间, 及进入前台的本地时间做差值来计算的, 当用户手动修改本地时间, 也会成为时间差错误的来源之一, 如果能在进入前台的时间再从服务器取一次数据, 或者记录服务器时间而不是本地时间, 也可以避免这一误差

滚动cell时出去文字闪烁

在给cell的模型赋值后, 最好手动调用一下countDownNotification方法, 保证及时刷新

///  重写setter方法
- (void)setModel:(Model *)model {
    _model = model;
    self.titleLabel.text = model.title;
    // 手动调用通知的回调
    [self countDownNotification];
}

倒计时为0后出现复用问题

在倒计时为0后, 应该回调给控制器, 从后台请求一次数据, 保证倒计时没有出现误差

if (countDown <= 0) {
          // 倒计时结束时回调
          xxxx(使用代理或block)
    }return;

出现每秒倒计时减2的问题

1.查看定时器设置是否正确, 或者通知是否监听了两次
2.在countDownNotification方法中, 是否用[NSDate date]做了某些计算, 因为[NSDate date]为当前时间, 每一秒去取都会比上一秒大一秒, 再加上timeInterval也是一秒加一, 那么就会出现每秒倒计时减2的问题

如果还有不懂的问题, 或者出现其它bug
请查看Demo: Demo
或者给我留言, 喜欢的话, 就给作者一个star

相关文章

网友评论

  • EI_Rey:当你对self.detailTextLabel.text进行赋值的时候,内存没问题,但是你在Cell上自定义了一个UILabel来显示倒计时时,内存会一直涨到App崩溃,但是对Cell自带的detailTextLabel进行赋值就不会有这个问题,100%复现啊,作者看看什么原因呢?
    EI_Rey:这个我找到原因了,自己创建的UILabel采用了Masonry布局,每次UILabel的text改变时都调用了layoutSubviews这个方法,计时器我改成了以毫秒为最小单位,每0.1秒就调一次这个方法,只能初始化UILabel的时候直接给定它的frame,这样内存就不会每秒增加1M了,我纳闷为什么频繁调用layoutSubviews内存会暴涨呢?
  • Shadowoff:有两个控制器A和B A和B都有倒计时,当A push到 B 怎么做把B的定时器关闭,而A的不关闭,在B 写[kCountDownManager invalidate]; A中的定时器也停止了
  • DamonYJ:看了代码, 楼主是使用CFAbsoluteTimeGetCurrent()
    CFAbsoluteTimeGetCurrent() 和 NSDate 差不多, 只不过是以GMT为标准的
    而且也会被用户修改系统时间而改变, 用来做商城秒杀不安全
    六听雪碧:我觉得时间可以取服务器的来计算倒计时
  • 你好牛:即使销毁定时器了 在开启时 原来的数据还在 又会重新开始 只有后台 才会完全清空 不知道你们遇到过没有
    大头herob:在销毁定时器的方法中, 我没有将数据清空, 如果想要清空数据, 可以手动调用reload方法
  • 你好牛:cell上面倒计时完成之后 会一直重复 好像有缓存似的 但是如果一个cell上面完成了 就销毁 会导致其他的也不走了 请问怎么解决
    大头herob:如果一个cell监听了倒计时的通知, 那么不管他是什么状态都会收到回调的, 如果是倒计时完成的状态, 你要在回调方法中手动处理它, 例如: 你不想做任何处理就直接return, 或者将通知移除掉
    你好牛:@大头herob return 了 没有用
    大头herob:@你好牛 完成了倒计时就将回调return了
  • 新新新用户:倒计时结束怎么一直不停的回调
    你好牛:@新新新用户 解决了吗)我也是
  • 8c8c9760d2b9:大佬,self.model.count 这个属性是什么意思?为什么的定时器数值一直不变?
    大头herob:@lisary getcurrent的值是当前时间吗? 那么当前时间会一秒一秒加,和定时器计的值也是一秒一秒加,所以相减的值没变
    8c8c9760d2b9:@大头herob 我只有一个倒计时失效时间failTime,timeInterval = [kCountDownManager timeIntervalWithIdentifier:failTime]; NSInteger countDown = [TimeUtils getCurrentTime].intValue - timeInterval;这样写定时器数值为什么一直不变?
    大头herob:@lisary 这个属性是请求数据返回的, 例如现在还60s就是60啊
  • hello_JSH:github✨已献上,很棒!!!
  • Arthurcsh:解决思路是不错, 但是有个问题,全局性CountDownManager notification name "OYCountDownNotification",一个项目创建多个CountDownManager通知消息就乱了
    Arthurcsh:@大头herob 一个项目需要倒计时的业务分插在多个模块里,用同一个单例来管理不太好
    大头herob:@Arthurcsh 所以CountDownManager使用单例呀
  • Juice007:我觉得解决Timer循环引用最方便的办法还是新建一个类别。OC大家可以看看这个:https://juejin.im/post/5a329003f265da4320034606?utm_source=gold_browser_extension
    swift大家可以看看这个:http://blog.csdn.net/viiimaus/article/details/77478317
  • 和影子玩拳击:有个问题想请教一下,这个定时器什么时候会移除呢?那个timeInterval会一直向上加吗?
    大头herob:@和影子玩拳击 是的, 定时器走, timeInterval就会一直向上加
    和影子玩拳击:@大头herob 是不是我不掉用[kCountDownManager invalidate]这个方法,他的timeInterval就会一直向上加
    大头herob:@和影子玩拳击 有个方法可以移除定时器, 需要手动移除
  • Yasin的简书:NSTimer可以精确到50-100毫秒.
    NSTimer不是绝对准确的,所以你使用时间累加的方法时间久了有可能成为时间误差的来源
    Yasin的简书:@大头herob 我是把当前时间赋给给定时器单例,倒计时用的是时间戳,和你的实现不太一样
    大头herob:@Yasin的简书 nstimer的误差这么大吗? 这方面没去了解过, 那你是使用哪种方式来做定时呢? CADisplayLink吗
  • Yasin的简书:你的倒计时计算代码是
    /// 计算倒计时
    NSInteger countDown = [self.model.count integerValue] - kCountDownManager.timeInterval;
    也就是说无论你的通知调用多少次,这个countDown都是不变的,
    出现每秒倒计时减2的问题就不在于通知注册多少次了,可能是定时器那边有问题。

    另外,"最好将监听通知的方法写在init中, 而不是写在awakeFromNib中",我对这个不是很理解,我没找到文档说awakeFromNib会被调用多次啊,为什么监听不能写在这里面呢?
    大头herob:@Yasin的简书 你说的对, 不是通知回调导致的, 我找时间再找找原因
    感谢
  • tah:你这个在真机上运行时,App从前台进入后台,在后台的时候,不走 countDownNotification 这个方法。timeInterval不累计了。iOS 8.0
    tah:我们月中要发版本:sob:
    大头herob:@tah :+1: 这个关系到后台模式下了, 有时间我再完善
    tah:在后台的时间没有算上去,就导致倒计时显示的剩余时间不准确。
  • 匆匆基兰:你好 首页怎么解决倒计时销毁
  • 春暖花已开:不错,赞一个
  • 6cddee706b65:楼主,我是用MJRefresh来刷新的,每次刷新之后,倒计时就会多减5秒左右,这是什么问题?
    6cddee706b65:@大头herob 是不是每次调[_tab reloadData]; 方法之前都需要调[kCountDownManager reload];这个方法?
    大头herob:@H_c5b9 你这里加上1秒的延时表示网络请求刷新吗?
    6cddee706b65:如果用你的Demo上:
    //模拟刷新的时间
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    [self.tab.refreshControl endRefreshing];
    });
    这样就是正确的
  • 爱掏蜂窝的熊:楼主,假设第一次有10个cell在倒计时,还没有结束,此时loadmore,又多了5个cell,这怎么处理两次时间差不一致的问题?
    大头herob:@爱掏蜂窝的熊 那这种情况 manager要增加新的timerInterval 属性, 前面10个对应一个timerInterval 属性, 后面的5个也要有对应一个timerInterval 属性, 或者用数组记录timerInterval, 根据下标去取
  • 091e03f6b9a1:楼主.这个怎么控制单个cell的开启呢
    大头herob:@louis_qin 单个cell就把不需要倒计时的cell中定时器的回调return即可
    IAMCifi:大兄弟 单个cell的开启 你弄好了没
  • 布袋的世界:完美解决 tableViewCell的倒计时功能!

本文标题:iOS 在cell中使用倒计时的处理方法(新)

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