美文网首页八宝粥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