iOS UITableViewCell 性能优化
每一个有使用列表 App 都会用到 UITableView 或 UICollectionView,这就意味着也会用到 UITableViewCell 或 UICollectionViewCell。因为 UITableView 和 UICollectionView 不是静止不动的,在与用户交互过程中,需要通过不断地滚动来更新数据,而更新数据的展示就需要通过更新 UITableViewCell 和 UICollectionViewCell 的展示数据。
以 UITableViewCell 为例,UITableViewCell 自带一个UIImageView 和两个 UILabel,但是这些控件的布局是由 UITableViewCellStyle 决定的,使用 setFrame: 也是没有效果。显然只有这三个控件和固定的几种样式,是满足不了大多数产品需求和美工设计的。这个时候,我们就会想到使用复合视图的方式来实现设计需求,即在 UITableViewCell 的基础上添加新的视图控件。
然而,使用复合视图并不是一个好的方案,这就意味着将增加了视图层次结构的嵌套层次。虽然视图层次的多层嵌套是难免的,也是必要的,但是深层次的嵌套结构势必影响性能。因为当父视图更新布局时,就会触发 setNeedsLayout: 方法,而当这个方法被触发时也会触发 layoutSubviews: 方法去更新所有子视图的布局。所以我们应当避免在视图层次结构中多层嵌套,尽量保持扁平化。
那么问题来了,不用复合视图要怎么解决复杂的列表设计呢?
那就在一个视图控件上实现要多个视图控件才能做到的事情。我们使用重写 UIView 的 drawRect: 方法来自定义渲染元素,将需要显示的图片和文字直接绘制到一张视图上。
自定义 UITableViewCell 的头文件 ImproveTableViewCell.h :
/**
绘制 cell
*/
@interface ImproveTableViewCell : UITableViewCell
/**
标题
*/
@property (nonatomic, copy) NSString *strTitle;
/**
图片组
*/
@property (nonatomic, copy) NSArray<NSString *> *arrImage;
/**
日期
*/
@property (nonatomic, copy) NSString *strDate;
@end
实现文件 ImproveTableViewCell.m :
@implementation ImproveTableViewCell
/**
重写视图方法绘制
@param rect 坐标大小
*/
- (void)drawRect:(CGRect)rect {
// 屏幕宽
CGFloat fScreenWidth = [[UIScreen mainScreen] bounds].size.width;
// 文本属性
NSDictionary *dicAttribute;
// 标题
dicAttribute = @{NSFontAttributeName: [UIFont systemFontOfSize:14], NSForegroundColorAttributeName: [UIColor colorWithWhite:0.2 alpha:1.0]};
[self.strTitle drawInRect:CGRectMake(10, 10, fScreenWidth - 20, 20) withAttributes:dicAttribute];
// 图片组
// 图片宽
CGFloat fImageWidth = (fScreenWidth - 40)/3;
for (int i = 0; i < 3; i++) {
UIImage *imgPicture = [UIImage imageNamed:self.arrImage[i]];
[imgPicture drawInRect:CGRectMake(10 + i * (fImageWidth + 10), 40, fImageWidth, fImageWidth * 3/4)];
}
// 日期
dicAttribute = @{NSFontAttributeName: [UIFont systemFontOfSize:13], NSForegroundColorAttributeName: [UIColor colorWithWhite:0.6 alpha:1.0]};
[self.strDate drawInRect:CGRectMake(10, 45 + fImageWidth * 3/4, fScreenWidth - 20, 20) withAttributes:dicAttribute];
// 分割线
[[UIColor colorWithWhite:0.9 alpha:1.0] set];
UIRectFill(CGRectMake(0, 65 + fImageWidth * 3/4, fScreenWidth, 5));
}
@end
UITableViewDelegate 和 UITableViewDataSource 的方法实现:
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
ImproveTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:gDrawID];
if (cell == nil) {
cell = [[ImproveTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:gDrawID];
}
cell.strTitle = indexPath.row%2 ? @"蜡笔小新--lol船长" : @"葛力姆乔·贾卡杰克";
cell.arrImage = indexPath.row%2 ? @[@"test1", @"test2", @"test1"] : @[@"test2", @"test1", @"test2"];
cell.strDate = indexPath.row%2 ? @"2018-03-30 大傻逼" : @"2018-03-31 小傻逼";
// 更新渲染
[cell setNeedsDisplay];
return cell;
}
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 100;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return ([[UIScreen mainScreen] bounds].size.width - 40)/4 + 70;
}
结果截图:
结果截图@2x.png
参考资料:《高性能 iOS 应用开发》