翻译继续进行。。。
创新互联长期为上千多家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为榆树企业提供专业的成都网站设计、做网站、成都外贸网站建设公司,榆树网站改版等技术服务。拥有十多年丰富建站经验和众多成功案例,为您定制开发。
图3-6中的UITableViewCell有4张图片,还有一个不同颜色的subview。subview是开发者通常使用的一种方式,如果你想要一个不同的背景色或使内部的view管理起来更加简单的话。这种方法会导致table view滚动时的性能问题,所以你应该尽量避免使用它。
现在,让我们来看一下使用新方法的源代码,我自己绘制view,不再使用subview。你会看到用这种方法实现时需要做的工作,然后我会总结不同技术的优点和缺点。示例源代码来自DrawingCellViewController这个工程。下面是它的主要源代码。
For UITableViewController:
- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"CellIdentifier";
CustomDrawingTableViewCell *cell = (CustomDrawingTableViewCell *) [self.tableViewdequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CustomDrawingTableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];}
[cell updateMyCell];
return cell;
}
正如你所看到的,UITableViewController的主要代码并没有变化。这个和标准的UITableViewCell的不同之处在于你是如何初始化你的cell。例如,
[[CustomDrawingTableViewCell alloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier];
相比
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
在自定义的UITableViewCell(例如,CustomDrawingTableViewCell)中
(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString
*)reuseIdentifier {
if (self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]) {
CGRect subFrame = CGRectMake(0.0, 0.0,
self.contentView.bounds.size.width, self.contentView.bounds.size.height);
drawingView = [[CustomDrawingView alloc] initWithFrame: subFrame];
drawingView.autoresizingMask = UIViewAutoresizingFlexibleWidth |UIViewAutoresizingFlexibleHeight;
[self.contentView addSubview:drawingView];
}
return self;
}
现在到了最重要的部分:如何绘制文本,图片和在view中进行控制。
CustomDrawingView.m
- (void)drawRect:(CGRect)rect {
self.backgroundColor = [UIColor whiteColor];
// Drawing code.
[self.userName drawInRect:CGRectMake(70,0, 95, 21) withFont:userNameFont
lineBreakMode:UILineBreakModeTailTruncationalignment:UIBaselineAdjustmentAlignBaselines];
// Drawing Image
[self.avatarImage drawInRect:CGRectMake(20, 5, 36, 34)];
// Drawing button
[self.button drawInRect:CGRectMake(50, 5, 36, 34)];
}
简而言之,在UITableViewController构造一个自定义的UITableViewCell和之前是一样的;你仅仅需要在在dequeue的时候判断cell是否为nil,如果为nil就初始化一个新的对象。在初始化方法的内部,你必须添加一个subview到cell的内容中。对于subview,你需要复写drawRect方法,然后drawInRect方法绘制文本或图片。
自己绘制view的代码之所以比从nib文件加载或用创建添加subview的方法快,最直接的原因是GPU(图形处理单元)运行了绘制代码。GPU在渲染和显示UI是非常快的;因此,绘制代码是处理复杂subviews的
最快的方法。
注意:非常重要的是设置CustomDrawingView的背景为白色。默认是黑色的。 |
从这些例子中你能学到什么?
从上面的两个例子中,你应该记住一些基本的知识。
使用ReuseIdentifier。它能帮助你提升性能。
尝试减少cell预加载过程中的工作,尤其是从文件IO或网络IO加载图片的时间和效率。这样可以在最短的时间内显示图片。
如果你的应用有很多的subviews或复杂的结构,考虑自己用代码来绘制。这样可以让GPU来加速整个过程。
警告:从上面的测试结果可以看出,fps的结果变得越来越好,几乎接近最理想的值60。但是,使用这种方法,你不能享有InterfaceBuilder构建UI的好处。你总是要自己计算位置和大小,然后把这些信息放在drawRect方法中。这样很快会导致维护和功能膨胀(在应用中添加过多的功能)的问题。因此,谨慎使用drawRect,避免过多优化。 |
其他技术
我已经讨论了table view 滚动时提升性能的重要技术。还有其他一些小技术你通常是不需要使用的,但是在这里我也会介绍。如果你能理解这些概念,你可以把这些技术应用到其他的情况下。
缓存高度
无论是否需要创建一个新的cell,你需要缓存rows的高度,因为这是TableView所需要的信息。如果你的cell的高度是固定的,你不必担心。然而,如果它不是固定的,你需要确保你的cell计算足够的快。
可以尝试使用如下代码:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
return 80;
}
避免使用下面的代码:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath {
for (int i = 0; i < 100; i++) {
// find the smallest possible height for the row
}
return smallestHeight;
}
当需要渲染cell或者在动画过程中需要编辑/重排序cell的时候,OS会多次运行第一段代码段。xiang像第二段代码中有一个循环,无论是否需要知道cell的高度,OS都会运行一个重复100次的循环。
透明度
如果有可能,使UITableView所有的layers和subviews保持不透明。当一个view是透明的,iOS需要渲染一个像素两次或多次,这是因为一个像素同时属于很多subviews。这是一个非常耗时的过程。
通过代码或者InterfaceBuilder能够很简单的做到。开发者应该多次检查所有的subviews是不透明的。图3-7显示了如何在cell中设置subview为不透明的checkbox。
对于自定义代码,你可以通过代码来设置,如下:
view.opaque = YES;
避免使用图形特效
避免在UIImage中使用复杂的图形特效(例如渐变)。你应该对CoreAnimation进行一些小的配置,然后使用它来检查图形特效,如图3-8和3-9。
编辑/重排序 性能
在前面的部分,我展示了直接绘制的方法,你能够显著的优化apps的性能。但是绘制的这种方法,在动画和重排序性能中,会有很多严重的问题。
当你使用subviews的时候,动画会变的很快,在动画过程中UIKit不会重绘或修改任何东西。因此,对于UIKit来说,使用subview比自己通过代码来绘制更快。如果在动画或重排序中,你通过代码自己绘制view,你必须再一次绘制然后重新填充到新view中。这样导致做了很多工作,包括代码的创建和维护。
当你在用UITableViewController遇到性能问题时,你必须进行一个权衡。我的建议是:如果你能确保不会有很多的subviews,或者你允许用户对cell进行编辑或重排序,那么就用subview这种方法。虽然可能会使得app运行慢一些,但也已经不错了。
总结
通过这里例子的源代码分析,你已经学会了很多提升性能的重要技术了。
使用NSLog和CoreAnimation进行仔细的测试:通过一个实际的例子,我让你看到了如何使用Instrument和benchmark工具有效的理解问题的实质,以及在每一步优化之后提升了多少性能。
恰当的重用cell:这是第一步,也是最重要的一步。重用一个cell非常简单,但是很多应用都会漏了这一步。因此,你如果有很多性能问题,确保多次检查这方面的问题。
恰当的 缓存/重用 图片/数据:另外一个重要的步骤就是当返回一个cell显示的时候,减少数据加载和逻辑处理的时间。
减少总的加载和计算时间:并不是仅仅只有IO会减慢和阻塞UI线程;任何数据的处理都会减慢这个过程。因此,你应该总是尽可能的减少这个过程的处理时间。
自行绘制cell:在渲染table view的时候,为了充分利用GPU计算密集型的优势,你应该考虑使用直接绘制的方法。这样会显著的提升渲染的速度,增加测量的性能;fps几乎接近最大。你可以通过复写drawRect方法绘制自己的cell,然后在每个元素中调用不同的方法绘制每个UI元素。
透明度:当开发者将他们的UI元素放进view的时候,这是经常会遇到的小问题。如果他们没有设置view为不透明,渲染的时候就要对同一个点绘制两次或多次。
缓存高度:这是开发者经常会犯的另一个小错误。每当需要一个新的cell,每次都会有两个主要的方法被调用。
避免图形特效:在cell中有越多的特效,渲染的过程就越慢。因此,你应该对此进行测试。你可以使用CoreAnimation来查看每个UI元素渲染的效率。
编辑/重排序性能:滚动性能优化,对于编辑或重排序来说,可能会带来一些问题,因为UIKit和动画框架已经多subview进行了优化。如果你是自己绘制的话,这些框架的优化将不起作用。