ios之UITableView如何优雅的插入数据: 旋转180°下拉加载cell顶置

一. 概述

一般项目里面用到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的高度.

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 概述在iOS开发中UITableView可以说是使用最广泛的控件,我们平时使用的软件中到处都可以看到它的影子,类似...
    liudhkk阅读 12,977评论 3 38
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 14,436评论 4 61
  • 1.badgeVaule气泡提示 2.git终端命令方法> pwd查看全部 >cd>ls >之后桌面找到文件夹内容...
    i得深刻方得S阅读 10,249评论 1 9
  • 本周关键字:找回节奏 九十天目标检视:九十天相约 1,断舍离,建立空间力及信息力。整理完印象笔记,把印象笔记框架与...
    叶子佟阅读 2,985评论 0 0
  • 喜欢漫画。尤其爱这些清简的素描画。每次看到漫画,总能呆呆想半天。小小一幅画中,寥寥几笔,却蕴藏着丰盈无比的世界。 ...
    侯玲玉阅读 3,573评论 0 4

友情链接更多精彩内容