zoukankan      html  css  js  c++  java
  • 详细整理iOS中UITableView的性能优化

    最近在微博上看到一个很好的开源项目,是关于如何优化UITableView的,加上正好最近也在优化项目中的类似朋友圈功能这块,思考了很多关于UITableView的优化技巧,所以决定详细的整理下对优化UITableView的理解,需要的朋友们可以参考借鉴。

    一、介绍

    iOS开发中,UITableView可能是平时我们打交道最多的UI控件之一,其重要性不言而喻。Android也是如此,Android中的ListView和UITableView是相同功能的一个控件,但是iOS的UITableView更为强大一点,原因就不说了,如果你学过Android就知道iOS中的UITableView使用起来是非常简单的,这也是峰哥喜欢iOS胜过Android的原因之一。今天研究的内容就是UITableView的优化。

    开始之前,你能说出几种UITableView的可优化项?cell复用(Android中经常称为ListView的重用,其实重用复用都是一个意思,由于峰哥之前做过Android的原因,有时候我经常说“重用”,后面万一说“重用”大家知道是“复用”的意思就行了)!除了cell重用呢?

    二、UITableView的性能优化

    1、cell复用

    复用很简单,这或许是所有iOS开发者最为熟知的一个优化内容,如下代码:

    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    static NSString *Identifier = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
    if (!cell) {
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
    }
    return cell;
    }

    但是,这样重用就完美了吗?

    我们经常在注意cellForRowAtIndexPath:中为每一个cell绑定数据,实际上在调用cellForRowAtIndexPath:的时候cell还没有被显示出来,为了提高效率我们应该把数据绑定的操作放在cell显示出来后再执行,可以在tableView:willDisplayCell:forRowAtIndexPath:(以后简称willDisplayCell)方法中绑定数据。

    注意willDisplayCell在cell 在tableview展示之前就会调用,此时cell实例已经生成,所以不能更改cell的结构,只能是改动cell上的UI的一些属性(例如label的内容等)。

    2、cell高度的计算

    这边我们分为两种cell,一种是定高的cell,另外一种是动态高度的cell。

    (1)定高的cell,应该采用如下方式:

    self.tableView.rowHeight = 88;

    这个方法指定了所有cell高度都是88的tableview,rowHeight默认的值是44,所以一个空的TableView会显示成这个样子。对于定高cell,直接采用上面方式给定高度,不需要实现tableView:heightForRowAtIndexPath:以节省不必要的计算和开销。

    (2)动态高度的cell

    我们需要实现它的代理,来给出高度:

    (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // return xxx
    }

    这个代理方法实现后,上面的rowHeight的设置将会变成无效。在这个方法中,我们需要提高cell高度的计算效率,来节省时间。

    自从iOS8之后有了self-sizing cell的概念,cell可以自己算出高度,使用self-sizing cell需要满足以下三个条件:

    (1)使用Autolayout进行UI布局约束(要求cell.contentView的四条边都与内部元素有约束关系)。

    (2)指定TableView的estimatedRowHeight属性的默认值。

    (3)指定TableView的rowHeight属性为UITableViewAutomaticDimension。

    (void)viewDidload {
    self.myTableView.estimatedRowHeight = 44.0;
    self.myTableView.rowHeight = UITableViewAutomaticDimension;
    }

    除了提高cell高度的计算效率之外,对于已经计算出的高度,我们需要进行缓存,对于已经计算过的高度,没有必要进行计算第二次。

    作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:519832104 不管你是小白还是大牛欢迎入驻,分享经验,讨论技术,大家一起交流学习成长!

    另附上一份各好友收集的大厂面试题,需要iOS开发学习资料、面试真题,可以添加iOS开发进阶交流群,进群可自行下载!

    3、渲染

    为了保证TableView的流畅,当快速滑动的时候,cell必须被快速的渲染出来。所以cell渲染的速度必须快。如何提高cell的渲染速度呢?

    (1)当有图像时,预渲染图像,在bitmap context先将其画一遍,导出成UIImage对象,然后再绘制到屏幕,这会大大提高渲染速度。具体内容可以自行查找“利用预渲染加速显示iOS图像”相关资料。

    (2)渲染最好时的操作之一就是混合(blending)了,所以我们不要使用透明背景,将cell的opaque值设为Yes,背景色不要使用clearColor,尽量不要使用阴影渐变等

    (3)由于混合操作是使用GPU来执行,我们可以用CPU来渲染,这样混合操作就不再执行。可以在UIView的drawRect方法中自定义绘制。

    4、减少视图的数目

    我们在cell上添加系统控件的时候,实际上系统都会调用底层的接口进行绘制,大量添加控件时,会消耗很大的资源并且也会影响渲染的性能。当使用默认的UITableViewCell并且在它的ContentView上面添加控件时会相当消耗性能。所以目前最佳的方法还是继承UITableViewCell,并重写drawRect方法。

    5、减少多余的绘制操作

    在实现drawRect方法的时候,它的参数rect就是我们需要绘制的区域,在rect范围之外的区域我们不需要进行绘制,否则会消耗相当大的资源。

    6、不要给cell动态添加subView

    在初始化cell的时候就将所有需要展示的添加完毕,然后根据需要来设置hide属性显示和隐藏。

    7、异步化UI,不要阻塞主线程

    我们时常会看到这样一个现象,就是加载时整个页面卡住不动,怎么点都没用,仿佛死机了一般。原因是主线程被阻塞了。所以对于网路数据的请求或者图片的加载,我们可以开启多线程,将耗时操作放到子线程中进行,异步化操作。这个或许每个iOS开发者都知道的知识,不必多讲。

    8、滑动时按需加载对应的内容

    如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。

    (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
    NSIndexPath *ip = [self indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];
    NSIndexPath *cip = [[self indexPathsForVisibleRows] firstObject];
    NSInteger skipCount = 8;
    if (labs(cip.row-ip.row)>skipCount) {
    NSArray *temp = [self indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, self.width, self.height)];
    NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];
    if (velocity.y<0) {
    NSIndexPath *indexPath = [temp lastObject];
    if (indexPath.row+33) {
    [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]];
    [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]];
    [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]];
    }
    }
    [needLoadArr addObjectsFromArray:arr];
    }
    }

    记得在tableView:cellForRowAtIndexPath:方法中加入判断:

    if (needLoadArr.count>0&&[needLoadArr indexOfObject:indexPath]==NSNotFound) {
    [cell clear];
    return;
    }

    滑动很快时,只加载目标范围内的cell,这样按需加载(配合SDWebImage),极大提高流畅度。

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
    点击此处,立即与iOS大牛交流学习

  • 相关阅读:
    FreeRTOS 任务栈大小确定及其溢出检测
    FreeRTOS任务优先级说明
    leetcode 263 Ugly Number
    L2,breakfast or lunch
    Redis(2)用jedis实现在java中使用redis
    L1,a private conversation
    Redis(1)在windows环境下的安装和测试
    springMVC的拦截器工作流程
    求交集,差集,并集,善用java的set
    java下发电子邮件demo
  • 原文地址:https://www.cnblogs.com/chengxyyh/p/13141320.html
Copyright © 2011-2022 走看看