UITableView优化那点事

forkingdog关于UITableView优化的框架其实已经能够应用在一般的场景,且有蛮多的知识点供我们借鉴,借此站在巨人的肩膀上来分析一把。

至于UITableView的瓶颈在哪里,我相信网上随便一搜就能了解的大概,我这里顺便提供下信息点:

1 //罪魁祸首 2 tableView:cellForRowAtIndexPath: 3 tableView:heightForRowAtIndexPath:

框架同样根据这两个痛点给出了解决方案:

高度计算

fd_heightForCellWithIdentifier:configuration方法
 1 - (CGFloat)fd_heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id cell))configuration {
 2     if (!identifier) {
 3         return 0;
 4     }
 5
 6     UITableViewCell *templateLayoutCell = [self fd_templateCellForReuseIdentifier:identifier];
 7
 8     // Manually calls to ensure consistent behavior with actual cells. (that are displayed on screen)
 9     [templateLayoutCell prepareForReuse];
10
11     // Customize and provide content for our template cell.
12     if (configuration) {
13         configuration(templateLayoutCell);
14     }
15
16     return [self fd_systemFittingHeightForConfiguratedCell:templateLayoutCell];
17 }

这里先是通过调用fd_templateCellForReuseIdentifier:从dequeueReusableCellWithIdentifier取出之后,如果需要做一些额外的计算,比如说计算cell高度, 可以手动调用 prepareForReuse方法,以确保与实际cell(显示在屏幕上)行为一致。接着执行configuration参数对Cell内容进行配置。最后通过调用fd_systemFittingHeightForConfiguratedCell:方法计算实际的高度并返回。

fd_systemFittingHeightForConfiguratedCell方法
 1 - (CGFloat)fd_systemFittingHeightForConfiguratedCell:(UITableViewCell *)cell {
 2     CGFloat contentViewWidth = CGRectGetWidth(self.frame);
 3
 4     // If a cell has accessory view or system accessory type, its content view‘s width is smaller
 5     // than cell‘s by some fixed values.
 6     if (cell.accessoryView) {
 7         contentViewWidth -= 16 + CGRectGetWidth(cell.accessoryView.frame);
 8     } else {
 9         static const CGFloat systemAccessoryWidths[] = {
10             [UITableViewCellAccessoryNone] = 0,
11             [UITableViewCellAccessoryDisclosureIndicator] = 34,
12             [UITableViewCellAccessoryDetailDisclosureButton] = 68,
13             [UITableViewCellAccessoryCheckmark] = 40,
14             [UITableViewCellAccessoryDetailButton] = 48
15         };
16         contentViewWidth -= systemAccessoryWidths[cell.accessoryType];
17     }
18
19     // If not using auto layout, you have to override "-sizeThatFits:" to provide a fitting size by yourself.
20     // This is the same height calculation passes used in iOS8 self-sizing cell‘s implementation.
21     //
22     // 1. Try "- systemLayoutSizeFittingSize:" first. (skip this step if ‘fd_enforceFrameLayout‘ set to YES.)
23     // 2. Warning once if step 1 still returns 0 when using AutoLayout
24     // 3. Try "- sizeThatFits:" if step 1 returns 0
25     // 4. Use a valid height or default row height (44) if not exist one
26
27     CGFloat fittingHeight = 0;
28
29     if (!cell.fd_enforceFrameLayout & contentViewWidth > 0) {
30         // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead
31         // of growing horizontally, in a flow-layout manner.
32         NSLayoutConstraint *widthFenceConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth];
33         [cell.contentView addConstraint:widthFenceConstraint];
34
35         // Auto layout engine does its math
36         fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
37         [cell.contentView removeConstraint:widthFenceConstraint];
38
39         [self fd_debugLog:[NSString stringWithFormat:@"calculate using system fitting size (AutoLayout) - %@", @(fittingHeight)]];
40     }
41
42     if (fittingHeight == 0) {
43 #if DEBUG
44         // Warn if using AutoLayout but get zero height.
45         if (cell.contentView.constraints.count > 0) {
46             if (!objc_getAssociatedObject(self, _cmd)) {
47                 NSLog(@"[FDTemplateLayoutCell] Warning once only: Cannot get a proper cell height (now 0) from ‘- systemFittingSize:‘(AutoLayout). You should check how constraints are built in cell, making it into ‘self-sizing‘ cell.");
48                 objc_setAssociatedObject(self, _cmd, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
49             }
50         }
51 #endif
52         // Try ‘- sizeThatFits:‘ for frame layout.
53         // Note: fitting height should not include separator view.
54         fittingHeight = [cell sizeThatFits:CGSizeMake(contentViewWidth, 0)].height;
55
56         [self fd_debugLog:[NSString stringWithFormat:@"calculate using sizeThatFits - %@", @(fittingHeight)]];
57     }
58
59     // Still zero height after all above.
60     if (fittingHeight == 0) {
61         // Use default row height.
62         fittingHeight = 44;
63     }
64
65     // Add 1px extra space for separator line if needed, simulating default UITableViewCell.
66     if (self.separatorStyle != UITableViewCellSeparatorStyleNone) {
67         fittingHeight += 1.0 / [UIScreen mainScreen].scale;
68     }
69
70     return fittingHeight;
71 }

这里作者考虑到了如果Cell使用了accessory view或者使用了系统的accessory type,需要减掉相应的宽度。接着判断如果使用了AutoLayout,则使用iOS 6提供的systemLayoutSizeFittingSize方法获取高度。如果高度为0,则尝试使用Frame Layout的方式,调用重写的sizeThatFits方法进行获取。如果还是为0,则给出默认高度并返回。

Cell重用

fd_templateCellForReuseIdentifier方法
 1 - (__kindof UITableViewCell *)fd_templateCellForReuseIdentifier:(NSString *)identifier {
 2     NSAssert(identifier.length > 0, @"Expect a valid identifier - %@", identifier);
 3
 4     NSMutableDictionary *templateCellsByIdentifiers = objc_getAssociatedObject(self, _cmd);
 5     if (!templateCellsByIdentifiers) {
 6         templateCellsByIdentifiers = @{}.mutableCopy;
 7         objc_setAssociatedObject(self, _cmd, templateCellsByIdentifiers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 8     }
 9
10     UITableViewCell *templateCell = templateCellsByIdentifiers[identifier];
11
12     if (!templateCell) {
13         templateCell = [self dequeueReusableCellWithIdentifier:identifier];
14         NSAssert(templateCell != nil, @"Cell must be registered to table view for identifier - %@", identifier);
15         templateCell.fd_isTemplateLayoutCell = YES;
16         templateCell.contentView.translatesAutoresizingMaskIntoConstraints = NO;
17         templateCellsByIdentifiers[identifier] = templateCell;
18         [self fd_debugLog:[NSString stringWithFormat:@"layout cell created - %@", identifier]];
19     }
20
21     return templateCell;
22 }

这里通过dequeueReusableCellWithIdentifier方法从队列中获取templateCell,并通过fd_isTemplateLayoutCell属性标识其只用来充当模板计算,并不真正进行呈现,最后通过关联对象的方式进行存取。

注意:这里通过dequeueReusableCellWithIdentifier进行获取,也就意味着你必须对指定的Identifier先进行注册,注册可以通过以下三中方法:

1.使用storyboard中的Cell原型 2.使用registerNib:forCellReuseIdentifier: 3.使用registerClass:forCellReuseIdentifier:

到这里最重要的几个方法已经讲完了,除此之外框架还针对获取的高度进行了缓存。缓存的方式分为两种 :

1 1.根据IndexPath进行缓存(fd_heightForCellWithIdentifier:cacheByIndexPath:configuration) 2 2.根据实体的唯一标识符进行缓存(fd_heightForCellWithIdentifier:cacheByKey:configuration)

总结:

UITableView优化方案其实还有很多,不同的场景选用不同的方案,实现效果达到预期,这才是我么最终的目标。我这里简单介绍下其他的优化的细节:

1.复杂界面的时候,我们可以尝试异步手动进行绘制。 2.针对超出屏幕的Cell进行预缓存 3.存在大量图片的时候,只针对目标范围内的图片进行异步加载并缓存结果。 4.设置Views/Layers为不透明。

时间: 2024-12-27 05:59:06

UITableView优化那点事的相关文章

iOS_UITableView性能优化那点事

UITableView在实际开发中使用频率实在是很高, 因此, UITableView的性能优化是必不可少的, 本文下面就略微总结一下UITableView性能优化那点事. 本文着重介绍具体方法, 原理的话在文章最后会给出一些链接, 有兴趣可以自行查看. 1. 关于数据绑定 很多新加入iOS的朋友喜欢把数据绑定写入在UITableView Data Source方法 - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPa

UITableView优化技巧

最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化UITableView的.加上正好最近也在优化项目中的类似朋友圈功能这块,思考了很多关于UITableView的优化技巧,相信这块是难点也是痛点,所以决定详细的整理下我对优化UITableView的理解. UITableView作为iOS开发中最重要的控件之一,其中的实现原理很是考究.Apple在这块的优化水平直接决定了iOS的体验能甩安卓几条街,哈哈,扯淡扯多了...好了,废话不多说,直接进入主题.首先来谈谈

UITableView 优化总结

最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化UITableView的.加上正好最近也在优化项目中的类似朋友圈功能这块,思考了很多关于UITableView的优化技巧,相信这块是难点也是痛点,所以决定详细的整理下我对优化UITableView的理解. UITableView作为iOS开发中最重要的控件之一,其中的实现原理很是考究.Apple在这块的优化水平直接决定了iOS的体验能甩安卓几条街,哈哈,扯淡扯多了...好了,废话不多说,直接进入主题.首先来谈谈

详细整理:UITableView优化技巧

最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化UITableView的.加上正好最近也在优化项目中的类似朋友圈功能这块,思考了很多关于UITableView的优化技巧,相信这块是难点也是痛点,所以决定详细的整理下我对优化UITableView的理解. UITableView作为iOS开发中最重要的控件之一,其中的实现原理很是考究.Apple在这块的优化水平直接决定了iOS的体验能甩安卓几条街,哈哈,扯淡扯多了...好了,废话不多说,直接进入主题.首先来谈谈

青岛网站优化公司,7天见效从此优化不是事

青岛网站优化公司可以满足不同的客户群体.如果你想要把自己的产品信息广布各大门户信息,让自己的产品信息随时随地都可以看到那么请继续往下观看: 青岛网站优化公司精准优化优势是什么? 1.各大搜索引擎首页排名推广! 2.7天就能见到效果!速度快! 3.100-1000个关键词全面覆盖! 4.承诺80%产品信息在首页!有保障! 5.无需任何操作!只管看效果报表! 6.精准潜在客户一网打尽! 7.最小预算赚取最大推广效益! 8.信息掉落免费更换关键词! 9.一对一专业客服,贴心服务! 10.报表实时更新!

[转] 详细整理:UITableView优化技巧

原文:http://www.cocoachina.com/ios/20150602/11968.html 最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化UITableView的.加上正好最近也在优化项目中的类似朋友圈功能这块,思考了很多关于UITableView的优化技巧,相信这块是难点也是痛点,所以决定详细的整理下我对优化UITableView的理解. UITableView作为iOS开发中最重要的控件之一,其中的实现原理很是考究.Apple在这块的优化

app 性能优化的那些事(二)

来源:树下的老男孩 链接:http://www.jianshu.com/p/2a01e5e2141f 这次我们来说说iOS app中滑动的那些事.iOS为了提高滑动的流畅感,特意在滑动的时候将runloop模式切换到UITrackingRunLoopMode,在这个过程中专心做跟滑动相关的工作,这也就是在滑动过程中为什么nstimer无法工作的原因,因为两个没在同一mode下面.但我们可能经常会遇到滑动不怎么流畅的情况,比如在项目中碰到在滑动tableview的时候不怎么顺畅,感觉有点不爽,即便

UITableview优化随笔(1)-提高加载更多内容时的效率

UITableView上拉加载更多的功能相信很多应用都会用到,类似朋友圈.微博这样的应用,tableView中的数据内容高度根据内容来变化,同时需要加载大量的数据(上拉加载更多),要怎样才能保证加载数据时的页面流畅呢? UITableView的原理和使用,以及其滚动帧率的优化,不是本篇blog要讨论的问题,这个在网上能搜到大量资料,这里不再赘述. 一般在实现上拉加载更多数据的实现思路是: 1.获取新的数据 2.在当前dataArray中添加这些数据 3.在tableView上显示这些数据 其中第

cocos2d-x性能优化的那些事

年前在对我做的项目做性能优化,虽然在开发中,性能问题是一直关注着的,但是这个东西依然需要在后期做一段时间的优化的,也遇到不少坑,在这里分享下,也记作笔记,另外也欢迎大家有这方面的问题经验在这里讨论. 性能的优化主要是亮点,内存的优化和运行效率的优化 1.内存的优化 说内存的优化,首先要知道有什么东西会占据程序的内存,可优化的主要是两部分:数据和资源,先说数据,做短连接的游戏客户端有两种处理数据的方式:一种是傻瓜式客户端,另一种是缓存式客户端,傻瓜客户端几乎可以做到0数据,因为他每个界面只是负责展