美文网首页八宝粥iOSeriOS Developer
iOS,cell中有倒计时情况的总结

iOS,cell中有倒计时情况的总结

作者: its程 | 来源:发表于2016-11-13 22:15 被阅读2995次

一.先去网上探索

自己的项目中用到了关于倒计时的功能,请求的列表数据中有些cell是需要显示倒计时的, 之前没有做过这个功能, 以为应该一会儿就搞定了。 事实 上也是一会儿就搞个倒计时的效果出来了,感觉自己萌萌哒, 也就不去管了,可是过了几天测试数据的时候发现了很严重的cell重用问题,倒计时数据会出现错乱。于是开始在网上搜索解决方案,总结了一下大概是这样实现:
  
  遍历请求到的数据源,然后开启定时器,每过一秒就遍历所有数据源,然后把里面的数据更改之后再重新赋值回去。

显而易见,每过一秒就遍历所有的数据源去自减数据很耗性能,而且我的项目中,并不是每个cell里都有倒计时,不需要做这么多多余的操作。

而且,我按照其中一种方法写的结果,虽然可以防止重用cell带来的数据混乱,但是快速滚动tableView的时候停下来之后,cell上的数据会有短暂的不对(就是上一个cell的倒计时数据)一秒之后才会变成正确的数据,另一方面定时器的销毁也成了问题,销毁之后全部cell都失去倒计时的功能,完全不符合需求。

二. 结合网上的示例自己的解决方案

基于上面的缺陷,我想到了一个方法可以保证性能消耗不那么大,而且数据也不会出现错乱。而且包括数据的请求,缓存本地,上拉加载和上拉刷新时候带来的倒计时问题。具体源代码可以去github上下载,其实都是很简单的实现方法只是可能之前没有人分享出来,如果你发现代码中的 bug,或者你有更好的方法,欢迎批评指正!
  这里写出核心的思想。如果你的项目和我一样有这些需求,那么可以给你作一个参考。

  • timer 的添加和销毁


    timer.gif
  • 数据不会重用

mix.gif

控制器中的代码

  • 首先遍历数据源,取出需要倒计时的数据模型
-(void)enumerateDatasourceCountDown
{
    for(int i = 0; i < self.dataSource.count; ++i)
    {
        DataModel *model = self.dataSource[i];
        if (model.countTime)
        {
            [self countDownModel:model andIndexPath:i];
        }
    }
}
  • 把倒计时的具体剩余时间和与之对应的indexPath保存起来在字典中
-(void)countDownModel:(DataModel *)model andIndexPath:(NSInteger )indexInteger
{
    //哪一行的数据有倒计时
    NSString *indexKey = [NSString stringWithFormat:@"%ld",indexInteger];
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    [dict setObject:indexKey forKey:@"indexPath"];
    [dict setObject:@(model.countTime) forKey:indexKey];
    
    //把模型里的倒计时储存在字典中 以行数index索引为key
    //添加定时器之前先判断这一行的数据是不是已经添加了定时器
    NSNumber *number = self.countDownTimeDict[indexKey];
    NSInteger numberInteger =  [number integerValue];
    
    //如果在倒计时的字典中取不到 value 说明还没有添加定时器
    if (numberInteger <= 0)
    {
        //添加定时器
        NSTimer *timer  =  [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(numberCutDown:) userInfo:dict repeats:YES];
        [self.timerArr addObject:timer];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        
        NSLog(@"第%ld行已经添加了定时器",indexInteger);
    }
    [self.countDownTimeDict addEntriesFromDictionary:dict];

  • 定时器会执行的方法 (在这个方法中修改模型的倒计时,因为模型决定了cell显示什么数据,所以更改模型之后再去刷新这一行就不会出现数据混乱)
-(void)numberCutDown:(NSTimer *)timer
{
    //取出对应倒计时
    NSString * indexInteger = timer.userInfo[@"indexPath"];
    NSInteger index = [indexInteger integerValue];
    
    DataModel *model = self.dataSource[index];
    //修改模型时间
    model.countTime --;
    //刷新界面
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
    [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
    if (model.countTime == 0)
    {
        NSLog(@"第%ld行的定时器销毁了",index);
        [timer invalidate];
        timer = nil;
        return;
    }
}

总结:

这种方式,是在控制器中每过一秒就修改数据源,然后再重新刷新这一行数据达到倒计时的效果,解决了上述的两个问题。

  • 在控制器中创建timer定时器,一个定时器管理一个cell,如果cell的倒计时结束了,那就停止定时器,而且不会影响到其他cell,也不会浪费性能。
  • 由于修改的是模型,所以避免了数据混乱。
  • 本文 Demo

相关文章

网友评论

  • ziyouzhe4:你好,你这个demo使针对没有table没有section时候可以, 如果table有section时候,会出现跳动问题,请问怎么解决呢?
    its程:@majianjie :+1:
    ziyouzhe4:@its程 我解决了, 你查一下 是iOS11的问题,禁用掉几句代码就好了,你试试 if (@available(iOS 11.0, *)) {

    UITableView.appearance.estimatedRowHeight = 0;
    UITableView.appearance.estimatedSectionFooterHeight = 0;
    UITableView.appearance.estimatedSectionHeaderHeight = 0;
    }
    its程:是会有跳动的情况的, 可以不让 tableview 有弹簧效果, 可是这样好像 UI 上不是很生动, 还没想怎么解决...
  • Shawn_Locke:想请教一下,我现在也是这么实现的倒计时,是倒计时发送短信验证码,现在是每秒钟都在刷新cell,但是我现在的cell上有一个textField,在刷新的时候,textField会失去焦点,并且消失,请问这种问题有什么解决思路吗?
    its程:@Shawn_Locke 如果你只有一个 cell 有 button 的话你就不要刷新 cell, 直接刷新 button 嘛, 至于 button 的隐藏和显示用 model 控制,这样绝对不会有 textField 失焦的情况. 还有一种解决方案直接抛弃 tableView 使用 ScrollView, 你觉得呢?
    Shawn_Locke:@its程 不是,但是表单很长,所欲用TableView来写
    its程:你是多个 cell 里都有验证码?
  • 5541a64778ef:pop界面的时候定时器能销毁吗?会不会有循环引用的问题?
    its程:@4140d18ee6fc 定时器没有销毁啊, 定时器对 VC 有强引用呢, VC 肯定也不会被销毁啊 就泄漏了
    PGOne爱吃饺子:@its程 您好,请问pop为什么会有内存泄漏啊
    its程:如果这个界面被pop是会有内存泄露的, 解决办法简书上也有很多,比如GCD, 和更改target等, 回头要仔细试一下,
    因为我们的业务逻辑问题我并没有考虑到这一点,感谢你的提醒!
  • 梁森的简书:有小demo吗?
    梁森的简书:@its程 看到了😄 谢谢啊
    its程:demo 在文中给出了 "具体源代码" 这几个字
  • f048b6828bbf:6666,刚好用得上,谢谢分享!

本文标题:iOS,cell中有倒计时情况的总结

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