iOS --- UITableView的优化技巧

本文探讨了UITableView的优化技巧,包括重用cell、避免cell的重新布局、提前计算和缓存cell内容、异步加载和绘制、局部更新、触摸事件优化等,旨在提升性能和用户体验。建议使用静态变量存储cell标识,预先布局并缓存cell属性,利用estimatedRowHeight减少计算,以及使用异步绘制和局部更新策略。
摘要由CSDN通过智能技术生成

在iOS开发中, UITableView是最常用到的复杂控件. 使用不难, 但想用好却不容易. 需要考虑到后台数据的设计, tableViewCell的设计和优化, 以及tableView的效率等问题.
本文主要介绍一下UITableView的常见优化技巧, 主要参考博客:
VVeboTableViewDemo.

tableView的优化主要思路是:
1. 异步渲染内容到图片。
2. 按照滑动速度按需加载内容。
3. 重写处理网络图片加载。
4. 缓存一切可缓存的, 用空间换时间.

重用cell

UITableViewCell的重用机制是最常见也是最有效的优化方式之一.
使用dequeueReusableCellWithIdentifier来实现布局相同的cell的重用, 也可以通过cellForRowAtIndexPath 直接复用某个cell. 如

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    WeiboTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (cell == nil) {
        cell = [[WeiboTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    [self drawCell:cell withIndexPath:indexPath];
    return cell;
]

因为cellForRowAtIndexPath方法调用非常频繁,初始化,上下滚动,刷新都要调用. 所以cell的标示声明为静态变量更好。

static NSString *cellIdentifier = @“Tracks”;

避免cell的重新布局

cell的布局填充等操作比较耗时, 一般可在创建时就布局好. 如可将cell单独放到一个class中WeiboTableViewCell, 重写其initWithStyle方法, 在其中将cell的布局设置完成.
创建cell完成之后, 调用drawCell往其中填充内容即可, 即将cell的布局及填充分开执行, 且尽量将要填充的data提前准备好.

- (void)drawCell:(WeiboTableViewCell *)cell withIndexPath:(NSIndexPath *)indexPath {
    NSDictionary *data = [datas objectAtIndex:indexPath.row]; // 提前缓存好cell中的内容
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    [cell clear];
    cell.data = data;
    if (needLoadArr.count>0 && [needLoadArr indexOfObject:indexPath]==NSNotFound) {
        [cell clear];
        return;
    }
    if (scrollToToping) {
        return;
    }
    [cell draw];
}

提前计算并缓存cell的属性及内容

因UITableView继承自UIScrollView, 因此其布局主要表现为Plain和Grouped两种风格. 需先确定其contentSize及每个cell的位置, 才会将其放进去. 如:

要显示100个cell,而当前屏幕只能显示5个. 则reload的时候,会先调用100次heightForRowAtIndexPath方法, 然后调用5次cellForRowAtIndexPath方法; 滚动屏幕时, 每当cell进入屏幕, 都会调用一次heightForRowAtIndexPath和cellForRowAtIndexPath方法.

  1. cellForRowAtIndexPath和heightForRowAtIndexPath是调用最频繁的方法, 要尽量少地调用这两个方法.
  2. cell填充与计算布局分离.

    cellForRowAtIndexPath只填充cell,
    heightForRowAtIndexPath负责计算高度, 将高度等布局缓存到数据源中.

  3. 对于富文本AttributedString等cell中内容, 可提前创建好, 进行数据缓存, 然后需要时直接往cell中填充即可.

  4. 使用estimatedRowHeight来预估高度, 防止浪费时间计算屏幕外边的cell, 如

    self.tableView.estimatedRowHeight = 88.

  5. cell内容的异步加载
    如web的内容异步加载, 图片配合SDWebImage缓存, 将网络请求结果缓存.

subView的绘制

如果有多个不同风格的cell, 可以给每种cell指定不同的重用标识符. 然后使用dequeue每次将其出列使用即可. 如:

NSString *cellId = [NSString stringWithFormat:@“Cell%d%d”, indexPath.section, indexPath.row]; 
  1. 少用addView给cell动态添加view, 减少创建subview的数量如cell大致布局相同, 则可以只定义一种cell, 在初始化时添加, 通过hidden来控制其中内容的显示. 如有可能, 尽量缓存subview.
  2. 慎用clearColor, 多个view层叠加渲染会消耗更多的时间, 所以尽量不要或者少用透明图层, 因系统将透明层与下面的view混合起来计算颜色, 渲染速度. 所以, 慎重使用clearColor.
  3. 尽量将opaque设为YES, 尽量将subview的opaque设为YES, 避免GPU对cell其中的内容进行绘制.
  4. 避免无用的CALayer渲染特效.
  5. 需要绘制阴影的时候,通过指定阴影的路径提高效率.
  6. 重载subView的drawRect方法如果定制cell的过程中需要多个小的元素的话,最好直接对要显示的多个项目进行绘制,而非采用添加多个subview.

UITableView的局部更新

我们常用[self.tableView reloadData]来进行tableView中的数据更新. 如果只是更新某个section的话, 可以使用reloadSections等进行局部的更新

self.tableView reloadRowsAtIndexPaths:<#(NSArray *)#> withRowAnimation:<#(UITableViewRowAnimation)#> 

[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];

tableView的异步绘制

对于复杂的tableView界面, 可考虑异步绘制. 使用dispatch_async和dispatch_sync配合, 将业务逻辑与UI绘制分开. 如:

//异步绘制
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        CGRect rect = [_data[@"frame"] CGRectValue];
        UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0);
        CGContextRef context = UIGraphicsGetCurrentContext();
//整个内容的背景
        [[UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1] set];
        CGContextFillRect(context, rect);
//转发内容的背景
        if ([_data valueForKey:@"subData"]) {
            [[UIColor colorWithRed:243/255.0 green:243/255.0 blue:243/255.0 alpha:1] set];
            CGRect subFrame = [_data[@"subData"][@"frame"] CGRectValue];
            CGContextFillRect(context, subFrame);
            [[
  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值