前言

UITableView 是我们开发中常用到的控件。其优化也是老生常谈的话题。笔者在这里抛砖引玉。


圆角问题

IM模块的头像, 笔者的项目用UIButton。

早就听说iOS 设置圆角会造成性能上的开销。设置cornerRadius和masksToBounds 会发生离屏渲染。

但在iOS 9后,苹果对圆角问题进行了优化。UIImageView里png图片通过以上属性设置圆角不会触发离屏渲染(在iOS 12.1下亲测)。但UIButton设置图片和圆角,UILabel设置layer.backgroundColor 均会造成离屏渲染。

iOS 高效添加圆角效果实战讲解

  • 一种方法是在drawRect中用CAShapeLayer 和 UIBezierPath。

这方法会导致内存暴增,还会离屏渲染。并没有优化,反而恶化了。

  • 还有一种方法,Core Graphics画出圆角矩形,UIImageView直接截取圆角图片。

此方法用CPU渲染。CPU渲染能力不如GPU,但圆角这种轻量级渲染,CPU还是能胜任的。重点是GPU离屏渲染需要上下文切换,严重时会造成卡顿。

此方法缺点,CPU以及内存 额外开销。


cell中部分view的复用

这里说的并不是cell的复用,而是cell中部分view 的复用。

IM模块中,消息发送状态view,有三种情况,发送中圈圈,发送失败感叹号,发送成功没有发送状态view。

由于大多数消息都是发送成功的,所以有 发送状态view 的cell比较少,一个界面可能最多就一两个状态view。每个cell都创建会浪费内存。

(当然日常项目,UIScrollView一样的view也可以类似思路优化)

思路:新建一个类ViewCache。两个数组,一个装着正在用的view,另一个装着缓存中的view。

当cell设置model时,如果发送失败状态,cell没有statusView,就取缓存数组取,缓存数组空就新建一个,并且放到正在用的view数组中。当不用时,就放回缓存数组中。

- (void)setModel:(CellModel *)model {switch (model.status) {case 失败:if (!self.statusView) {self.statusView = [self.viewCache dequeueStatusView][self.contentView addSubView:self.statusView];}self.statusView.frame = model.layout.statusViewFrame;break;case 发送中:// 差不多break;default:if (self.statusView) {[self.viewCache removeStatusView:self.statusView];[self.statusView removeFromSuperview];}break;}
}
复制代码

高度计算

先来了解数据源、代理方法的调用时机。

网上有文章iOS开发-简单科普下UITableView和UICollectionView代理执行顺序说heightForRowAtIndexPathcellForRowAtIndexPath前。笔者下载Demo来测试确实如此。

但笔者自己写了一份Demo,亲测并不是。

于是在一篇文章tableView代理方法执行顺序中发现真相。

其实文档中也说清了,实现了预期高度,实际高度方法会被延迟到 cell将要显示时 调用。

对于固定高度的cell,直接设置rowHeight,不要实现代理cell高度方法。

我们知道 实现代理方法后, rowHeight 会失效。所以笔者伪一下代码(当然无凭无证乱猜)

    if(self.delegate && [self.delegate respondsToSelector:@selector(tableView:cellForRowAtIndexPath:)]) {return [self.delegate tableView:self cellForRowAtIndexPath:indexPath];} else {return self.rowHeight;// 默认高度44}
复制代码

所以我们也就能节省两个方法(respondsToSelector和高度方法)的开销。(苹果有没有针对这部分做优化不得而知)

对于动态高度的cell

动态高度有两种方法,一种是利用AutoLayout,另一种是直接算frame。

  • AutoLayout

iOS-谈一谈自适应Cell的高度缓存

简单的说,设置预算高度和estimatedRowHeight = UITableViewAutomaticDimension,然后cell中最下面的控件设置底部约束,撑开cell。

这方法不用实现高度代理方法,滑动条会在滚动过程中重新调整。

但是AutoLayout最终需要转成frame。这里就无可避免开销比直接算frame大。所以如果cell很复杂,不建议用AutoLayout。

缓存高度需要用以下两个方法,返回Auto Layout后内容高度。

- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize NS_AVAILABLE_IOS(6_0);
- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize withHorizontalFittingPriority:(UILayoutPriority)horizontalFittingPriority verticalFittingPriority:(UILayoutPriority)verticalFittingPriority NS_AVAILABLE_IOS(8_0);
复制代码

具体实现

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {CellModel * model = self.models[indexPath.row];return model.cellHeight ?: UITableViewAutomaticDimension;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {CellModel * model = self.models[indexPath.row];TestCell * cell = [TestCell cellForTableView:tableView model:model];//高度缓存if (!model.cellHeight) {CGFloat height = [cell systemLayoutSizeFittingSize:CGSizeMake(tableView.frame.size.width, 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel].height;model.cellHeight = height;}return cell;
}
复制代码
  • 另一种直接算frame。

在model中,设置和布局相关的属性,cellHeight懒加载。(笔者项目封装了一个CellLayout对象,包含每个控件的frame以及cell高度)

- (CGFloat)cellHeight {if (!_cellHeight) {CGFloat iconH = 20;CGFloat contentH = [self contentH];//算出来_cellHeight = iconH + contentH + 10;}return _cellHeight;
}
复制代码

然后在代理方法中

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {CellModel *model = self.models[indexPath.row];return model.cellHeight;
}
复制代码

iOS开发之UITableview之多种Cell高度自适应实现方案的UI流畅度分析


笔者能力有限,除了以上的优化策略,其实UITableView还有很多能优化的地方。以后笔者如果有机会,会尝试往以下方向优化。

  • 利用RunLoop空闲时间,预计算未显示的Cell高度。(可以参考SDWebImage)
  • 异步绘制Cell。
  • 滑动手指松开时,描绘计算要显示的Cell。

参考

  • iOS 保持界面流畅的技巧

转载于:https://juejin.im/post/5c80fcd76fb9a049b781269f

UITableView 优化相关推荐

  1. UITableView优化

    UITableView优化的那些事儿 作为iOS开发,UITableView可能是平时我们打交道最多的UI控件之一,其重要性不言而喻.关于TableView,我想最核心的就是UITableViewCe ...

  2. UITableView优化之按需加载

    滑动UITableView时,按需加载对应的内容 直接上代码: //按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载. - (void)scrollViewWill ...

  3. (0074)iOS开发之UITableView的优化

    写的很好引用 https://www.jianshu.com/p/af6b095aaaf3 前言 这篇文章对 UITableView 的优化主要从以下3个方面分析: 基础的优化准则(高度缓存, cel ...

  4. UITableView的优化技巧

    这段时间也看了很多关于tableview优化的文章,加上前段时间自己也做了一个同时仿微博和支付宝的项目,思考了一些关于UITableView的优化技巧.UITableView是iOS开发中最常用的控件 ...

  5. 刷新UITableView

    [from]http://www.superqq.com/blog/2015/08/18/ios-development-refresh-uitableview/ UITableView对于iOS开发 ...

  6. swif之UITableViewCell和UITableView常见属性复习

    1.UITableViewCell常见的属性: imageView 单元格图片 textLabel 图片右边的标签 detailTextLabel     右边标签下的小标签 accessoryTyp ...

  7. 一个iOS程序员的BAT面试经验

    转载于:http://www.techug.com/ios-bat-interview 随着各大公司春招的开始,很多小伙伴都行动起来了,我有幸能够加入百度并和大家分享自己的经验心得.由于我面试的都是比 ...

  8. iOS每日总结博客版:iOS开发历程中了解和学习的文章

    2019独角兽企业重金招聘Python工程师标准>>> #iOS开发进阶指导:入门->进阶->大神 top:入门无忧网,各语言ide入门 http://www.rm5u. ...

  9. 800000000000

    8000000000题@TOC 不属于使用SDWebImage步骤的是 A:下载SDWebImage的最新SDK​ B:把SDK导入工程中BackgroundColor​ C:关闭arc​ D:如果需 ...

最新文章

  1. 条件选择结构:星期计划(switch)
  2. 在什么情况下我应该使用malloc和/或new?
  3. jquery生成一个li_JQuery实现ul中添加LI和删除指定的Li元素功能完整示例
  4. 单日2000W+订单,如何忙中不错?美团外卖业务异常检测实践详解
  5. 致:WWF技术博客领跑者WXWINTER--兰竹梅菊.春夏秋冬
  6. css 控制溢出文本显示省略号效果
  7. 移动数据网络类型是nr_便携式移动网络的快速搭建方法
  8. css3:border-radius圆角边框详解 (变圆 图片)
  9. 深度Linux对比比,深度Linux10.06 beta1与ylmfOS 3.0简单对比
  10. Dynamics AX2012 标准权限控制工作原理
  11. 计算机教学的弊端,信息技术在教学中的利弊及解决对策
  12. 基于nodejs,tinypng的压缩图片工具
  13. 计算机检索的主要方法,计算机检索主要途径和方法
  14. htm html mht 无图标,mht文件与html文件有何区别?
  15. java发送公众号/服务通知模板消息到指定用户(完整流程|亲测可用)
  16. 七牛服务器提供的压缩图片的方法
  17. Kali Linux虚拟机安装
  18. php hook类,基于 CodeIgniter 构建 JWT RESTfull API Server
  19. 云计算与大数据课程学习笔记
  20. 新加坡打造绿色数据中心任重道远

热门文章

  1. axios学习笔记(二):轻松弄懂XHR的使用及如何封装简易axios
  2. IE6不支持min-heigt的bug解决的办法
  3. LeetCode--33. 搜索旋转排序数组(二分法)
  4. OpenCV3学习(11.6) ORB特征检测器及BRIEF描述符
  5. python while无限循环、人为终止_Python while while循环永远不会停止,即使它应该
  6. 控制台输入一个整数,取该整数的各位数,判断其是否能被该整数整除,能则返回true,不能则返回false
  7. flume 一对多hdfs_10PB 规模的 HDFS 数据在 eBay 的迁移实战
  8. 为什么python的命名不能以数字开头,Python模块名称以数字开头
  9. 安装composer
  10. 洛谷 题解 P1135 【奇怪的电梯】