美文网首页iOS
ios之UITableView如何优雅的插入数据: 旋转180°

ios之UITableView如何优雅的插入数据: 旋转180°

作者: 大风先生bigWind | 来源:发表于2017-11-20 17:25 被阅读0次

    一. 概述

    一般项目里面用到UITableView的概率还是灰常大的, 我的项目从一开始也用了. 大概的来说就是类似一个收件箱的功能, 推送来一条消息就加一个cell, 这是很简单的. 蓝鹅, 接下来经历了两次需求更改:

    1. 增加'数据本地化'的功能;
    
    2. 增加查看历史消息功能, 也就是下拉加载更多的消息.
    

    第一个还好说, 就是用FMDB弄了个简单的数据库, 差不多就是获取数据的途径变成从数据库获取而已;
    在增加了第一个需求之后, 叠加提出第二个需求, 看起来也很简单哦, 不就是加数据嘛. 但是, 当我开始做起来的时候才发现, 坑蛮多的.

    这篇文章是基于我自己项目所写的, 可能在一些地方处理比较特殊并不具备"普世价值"; 但从开发者学习的角度来说, 我始终认为思路比代码重要. 所以这里我可能在思路方面比较啰嗦点, 直接要代码的慎重选择啊.
    先说一下, 需求二里面主要遇到的坑有两个:

    坑一 : 下拉加载的10条消息, 显示直接在第0-4条, 要看第5-9条的话又得反方向上滑
    

    为了解决坑一, 将tableview和cell都旋转了180°, 这时候, 坑二来了

    坑二 : 在初始cell数量较少的时候, 推送新消息过来增加cell的时候导致界面跳动
    

    二. 项目过程

    2.1需求分析
    由于第一个需求太过简单, 所以就忽略啦. 这里直接讲第二个需求的.
    刚刚上面讲的不是很清楚, 这里补充一下第二个需求的具体:

    1.进入到界面的时候, 里面要先显示最多10条消息;
    2.下拉可以加载历史数据(每次10条);
    3.当有新消息推送过来的时候, 要添加在最底部, 并滚动到最底部;
    4.三日内做完.
    

    刚看到这个需求的时候, 我的第一个反应是这太TM简单了, too simple! 看老夫如何在三天时间以内用半天做完再用两天半假装没做(tou)完(lan)😏😏.
    2.2 坑一
    蓝鹅, 忙活半天过后, 我发现自己too young too naive😂😂. 这就是上面说的坑一, 图是这样的, 可以看到点击"消息+1"的时候还算正常, 但是下拉加载更多消息(10条)的时候, 都是停留在最顶部, 也就是上面坑一说的, 要看第5-9条的时候, 得反方向上滑

    正常的.gif

    为了解决坑一, 我尝试过调试NSIndexPath并且使用scrollToRowAtIndexPath:<#(nonnull NSIndexPath *)#> atScrollPosition:<#(UITableViewScrollPosition)#> animated:<#(BOOL)#>]方法来各种折腾....再过了半天之后😒😒放弃了, 下班, 对, 下班!!!谁说程序猿都得加班的😁😝.

    2.3 坑二
    昨晚想了一(yi)下(晚)和简单的实践(说好的不加班呢, 变相加班😭😭!), 发现在uitableview的底部使用inset...和scroll...方法拼接数据很完美, 脑洞大开," 独创"(后来发现网上也有)ios大法之乾坤大挪移----将整个tableview旋转180°!!!!! 当然cell也要旋转180°.
    旋转的代码分别是:

        CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI);
        [tbV setTransform:transform];
    

    cell的旋转:

            CGAffineTransform transform =CGAffineTransformMakeRotation(M_PI);
            [self.contentView setTransform:transform];
    

    当然, 使用mjrefresh的话, 就得用原来使用的mj_header换成mj_footer了(关于mjrefresh的使用).

    弄好之后是这样的, cell直接在最底部:


    倒过来一.gif

    这明显是不行的, 为了使得cell数量比较少的时候可以顶置(实际是底置, 因为tableview旋转了180°了), 于是想到了根据tableView.contentSize.height(关于 tableView.contentSize你想了解的)来设置tableView.contentInset, 代码是这样的

    - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
    
        if (tableView.contentSize.height < tableView.frame.size.height)
        {
            
            tableView.contentInset = UIEdgeInsetsMake(tableView.frame.size.height - tableView.contentSize.height - RSRealValue(120), 0, 0, 0);
        }
    }
    

    运行出来一看, 哇, 完美解决!!!! 蓝鹅, 高兴地太早了😂😂. 这时候下拉加载时没问题的, 但新加一条消息的时候, 新的cell会把原来的cell顶上去, 而不是拼接在最下面, 如图:

    倒过来contentInset.gif
    经过一番思考, 本人大致的认为, 在新加入cell的时候, 原来的tableView.contentSize还是没变, 也就是说tableView.contentSize的布局其实还是只到了上一个cell的底部, 并以此往复, 所以出现了上图里面加了3条新消息再下滑的时候才出现😢😢.
    这时候, 又想到了一个方法, 可以尝试监听tableView.contentSize还动态修改tableView.contentSize.....&%$#@!@#$%&%$过程省略几百字, 直接说结果吧, 然并卵. 而且, 在ios的UITableView里面, 并不是很推荐修改tableView.contentSize, 因为这个是苹果自身维持动态平衡的, 我们强心修改的话会影响其平衡, 而且在控制台会出现警报. 所以很不推荐.
    就这样又折腾了半天.....下午整个人都在想这个问题. 终于在吃了一颗糖果之后, 想到一个方法:
    为啥不通过记录cell的总高度来代替tableView.contentSize, 然后设置一个透明的tableheaderview来代替设置tableView.contentInset??????快被自己的机智感动了😁😁.
    这样既可以绕过上面说的问题了. 说干就干.
    需要在cell的model文件里面增加一个记录本cell高度的属性:
    // 计算出来的cell的高度
    @property (nonatomic, assign) CGFloat cellHeight;
    

    同时, 也得在控制器文件里面增加一个记录所有cell总高度的属性:

    // cell的叠加高度
    @property (nonatomic, assign) CGFloat cellsTotalHeight;
    

    并且写了它的懒加载方法, 这样每次在获取总高度的时候都能保证是最新的

    - (CGFloat)cellsTotalHeight{
        
        CGFloat heightAll = 0;
    
        for (int i = 0; i < self.dataMulArr.count; i ++) {
            
            OneModel *model = self.dataMulArr[i];
            
            heightAll = heightAll + model.cellHeight;
        }
        
        _cellsTotalHeight = heightAll;
        
        NSLog(@"cell总高度: %f",heightAll);
        
        return heightAll;
    }
    

    同时, 返回头部视图和高度是这样de(为了方便调试, 先把头部视图设置成有个半透明色)

    #pragma mark- 返回头部视图
    - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
    
        UIView *headV = [[UIView alloc] init];
    //    headV.backgroundColor = [UIColor clearColor];
        headV.backgroundColor = RSColorFromRGBA(0x00FF00, 0.5);
        return headV;
    }
    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{
        
        NSLog(@"头部视图高度中cell: %f", self.cellsTotalHeight);
        
        CGFloat headHeight = 0;
        
        if (self.cellsTotalHeight < self.tbV.frame.size.height)
        {
            
            headHeight = self.tbV.frame.size.height - self.cellsTotalHeight - RSRealValue(240);
        }
    
        return headHeight;
        
    }
    

    跑出来是这样的:


    倒过来加header.gif

    这时候再把tableheaderview的背景色改成透明的, 就成了这样了:


    倒过来加header透明.gif

    果然完美解决了😂😂, 这时候其实只剩一天时间假装没完(偷)成(lan)了.

    不过还是要注意一下, 看图倒过来加header.gif将tableheaderview改成透明之后, 新增cell还是会被tableheaderview挡住, 所以最后一个cell是点击不到的. 因为我项目里面的cell不需要点击, 所以这个问题我可以忽略; 如果小伙伴项目里面的cell需要点击的话, 就要将tableheaderview高度进一步减去新增cell的高度哦. 这里我就不做具体代码了, 小伙伴们自己探索一下把.

    三. 小总结

    总结起来, 这里面几个关键点是:

    1.旋转180°
    
    2.不使用reloaddata方法, 而是使用inser....和scroll...两个方法
    
    3.通过增加tableheaderview来实现倒转tableview之后cell的顶置, 并且动态的改变tableheaderview的高度.
    

    这个虽然只是项目的一小部分, 但每次解决问题之后都能获得成就感, 这就是码农单纯的快乐吧.

    相关文章

      网友评论

        本文标题:ios之UITableView如何优雅的插入数据: 旋转180°

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