tableView 的性能优化

tableView可以说是每个app中必不可少的控件,所以掌握流畅度优化技能相当的重要。


这里总结一些常用的优化技巧,分享给大家:


① cell内部控件的层次结构尽量的少,可以使用drawRect画;


② 控件尽量不要有透明度,因为如果上层控件有透明度的话,系统会努力的绘制下层控件的内容与上层控件的内容,并且将两个内容按照透明度去进行绘制,十分耗性能;


③ 栅格化

将cell内容渲染成一张图片,在滚动的时候就是一张图片:


layer.shouldRasterize      =  true;  // 栅格化cell,滚动时只显示图片

layer.rasterizationScale  =  [UIScreen mainScreen].scale;  // 默认缩放比例是1,要适配当前屏幕

在Instruments中调式可以看到cell已经是黄色,说明已经渲染成一张图片:



栅格化cell.gif

④ 异步绘制cell的layer,如果cell比较复杂时可以使用


layer.drawsAsynchronously = true;

官方文档注释:

/* When true, the CGContext object passed to the -drawInContext: method

* may queue the drawing commands submitted to it, such that they will

* be executed later (i.e. asynchronously to the execution of the

* -drawInContext: method). This may allow the layer to complete its

* drawing operations sooner than when executing synchronously. The

* default value is NO. */

@property BOOL drawsAsynchronously

  CA_AVAILABLE_STARTING (10.8, 6.0, 9.0, 2.0);

意思就是如果使用异步,cell会提前绘制。

⑤ 在cell中不要用layer去画圆角

CALayer的cornerRadius是一个超级费性能的东西,它会在每一帧都裁剪圆角,无论你有没有滚动视图都会运算裁剪圆角,很费GPU性能!所以要让CPU去做圆角图片!

可以在UIImage分类中,开启上下文,利用贝塞尔路径画圆角:


#import "UIImage+Extension.h"


@implementation UIImage (Extension)


- (void)qv_cornerImageWithSize:(CGSize)size fillColor:(UIColor *)fillColor completion:(void (^)(UIImage *))completion {


    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        UIGraphicsBeginImageContextWithOptions(size, true, 0);

        CGRect rect = CGRectMake(0, 0, size.width, size.height);

        [fillColor setFill];

        UIRectFill(rect);

        UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:rect];

        [path addClip];

        [self drawInRect:rect];

        UIImage *result = UIGraphicsGetImageFromCurrentImageContext();

        UIGraphicsEndImageContext();

        dispatch_async(dispatch_get_main_queue(), ^{

            if (result != nil) {

                completion(result);

            }

        });

    });

}


@end

参考文章

http://www.cocoachina.com/ios/20150803/12873.html

http://blog.csdn.net/shaobo8910/article/details/46779259

也可以让服务器去处理圆角图片,这样我们就不需要再去操作。

注意:iOS9.0之后,.png图片直接设置圆角是不会产生离屏渲染,iOS9.0之前还是会离屏渲染的。


⑥ 缓存行高

如果是自动布局计算行高很消耗CPU,每次滚动到该cell都要计算self.contentView.layoutIfNeeded,注意要移除contentView的底部约束。建议复杂的cell不要用自动布局。


⑦ cell内部所有显示的数据提前准备好,尽量少实时计算。所有的控件大小提前计算好,不要每一次都计算。


⑧ 按需加载,按照用户滚动的速度去选择加载哪个cell

原理:在快速滑动松手后滚动的cell个数超过预定的个数,只显示最后出现的cell的前三个cell,把这三个cell的indexPath存到数组中,在数据源方法里判断如果数组count>0,且数组不包含当前的indexPath,那就说明此cell是在快速滑动中需要隐藏的:

代理方法:


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

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{

    NSIndexPath *ip = [_titleTableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];

    NSIndexPath *cip = [[_titleTableView indexPathsForVisibleRows] firstObject];

    NSInteger skipCount = 1;    // 这里我为了方便演示写的1,大家可以按需求自行设定

    if (labs(cip.row-ip.row)>skipCount) {

//        此方法可以获取将要显示的组

//        visibleSections = [NSSet setWithArray:[[_titleTableView indexPathsForVisibleRows] valueForKey:@"section"]];

        NSArray *temp = [_titleTableView indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, _titleTableView.frame.size.width, _titleTableView.frame.size.height)];

        NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];

        if (velocity.y<0) {      // 上滑

            NSIndexPath *indexPath = [temp lastObject];

            if (indexPath.row+3<numberOfRows*numberOfSections) {

                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+3 inSection:indexPath.section]];

                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+2 inSection:indexPath.section]];

                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section]];

            }

        } else {                // 下滑

            NSIndexPath *indexPath = [temp firstObject];

            if (indexPath.row>3) {

                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:indexPath.section]];

                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:indexPath.section]];

                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:indexPath.section]];

            }

        }

        [needLoadArr addObjectsFromArray:arr];

    }

}

相应的,每次开始拖动的时候去清空数组。还有种情况,如果界面上有显示空白cell的时候突然手动停止滚动呢?


- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

    [needLoadArr removeAllObjects];    // 清空数组

    // 取到当前界面上能显示的indexPaths,判断是否有隐藏

    NSArray <NSIndexPath *>*indexpaths = [_titleTableView indexPathsForVisibleRows];

    UITableViewCell *firstCell  =  [_titleTableView cellForRowAtIndexPath:indexpaths.firstObject];

    UITableViewCell *lastCell  =  [_titleTableView cellForRowAtIndexPath:indexpaths.lastObject];

    //  在当前可见的区域中,第一个cell或者最后一个cell是隐藏状态,那么重新加载可见区域内的cell

    if (firstCell.isHidden == true || lastCell.isHidden == true) {

        [_titleTableView reloadRowsAtIndexPaths:indexpaths withRowAnimation:UITableViewRowAnimationNone];

    }

}

也可以把判断的代码写在scrollView停止滚动监听方法里,但是个人觉得没必要,因为这种情况必定是手动触碰去停止的,这里处理没问题

数据源方法:


  if (needLoadArr.count > 0) {

        if (![needLoadArr containsObject:indexPath]) {

//            NSLog(@"该cell是快速滑动中的cell,所以隐藏");

            cell.hidden = true;

            return cell;

        }

    }

    cell.hidden = false;      // 正常显示的cell

按需加载参考demo:https://github.com/johnil/VVeboTableViewDemo 这位前辈在tableView优化上做到了极致


⑨ 其他:

尽量少的使用富文本;

时间格式化对象使用同一个;


总结:tableView性能优化的方式有很多,但不是所有的我们都需要。比如不是必需要显示的界面,预先计算行高就是浪费。我们开发者应当结合实际情况,从用户的角度出发,这是做一个优秀App最基本也是最核心的思想。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容