TableView是App里最常用的一个UI控件了,所以优化TableView性能,是我们最常见的一个问题,也是面试常遇到的问题。
1.设置Cell的高度
设置Cell的高度有两种方式:
//1.属性,
@property (nonatomic) CGFloat rowHeight;
//2.代理
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
如果用代理设置了Cell的高度,那么设置属性就不起作用了。如果是固定行高,强烈建议用属性。因为TableView里面有几个Cell,代理的方法就会被执行几次,严重影响效率。
如果cell的高度根据内容的变化而变化怎么办,用代理设置,并且当数据请求下来的时候,计算好Cell的高度,不要在代理方法里面计算。
FHMessageModel.h
#import <Foundation/Foundation.h>
@interface FHMessageModel.h :NSObject
@property (nonatomic, assign) CGFloat rowHeight;
@end
FHMessageModel.m
- (CGFloat)rowHeight
{
// 已经计算过,就不用在计算了
// 这个判断必须有,否则和在代理里每次都计算是一样的
if (_rowHeight > 0) {
return _rowHeight;
}
// 计算Cell的高度
....
return _rowHeight;
}
3.异步绘制
异步绘制,就是为Cell设置一个背景图片,然后可以在Cell上异步绘制Cell的内容。这样可以减少Cell的层级,和相互之间的约束。
红色方框里的内容、线和背景色都是异步绘制的。
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// 设置背景图片
postBGView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.contentView insertSubview:postBGView atIndex:0];
}
//将主要内容绘制到图片上
- (void)draw{
if (drawed) {
return;
}
NSInteger flag = drawColorFlag;
drawed = YES;
// 在全局线程中异步绘制Cell的内容
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Cell 的frame
CGRect rect = [_data[@"frame"] CGRectValue];
UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
// 绘制Cell的白色背景
[[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);
// 绘制减肥女孩上面那条分隔线
[[UIColor colorWithRed:200/255.0 green:200/255.0 blue:200/255.0 alpha:1] set];
CGContextFillRect(context, CGRectMake(0, subFrame.origin.y, rect.size.width, .5));
}
{
float leftX = SIZE_GAP_LEFT+SIZE_AVATAR+SIZE_GAP_BIG;
float x = leftX;
float y = (SIZE_AVATAR-(SIZE_FONT_NAME+SIZE_FONT_SUBTITLE+6))/2-2+SIZE_GAP_TOP+SIZE_GAP_SMALL-5;
// 绘制技能酱
[_data[@"name"] drawInContext:context withPosition:CGPointMake(x, y) andFont:FontWithSize(SIZE_FONT_NAME)
andTextColor:[UIColor colorWithRed:106/255.0 green:140/255.0 blue:181/255.0 alpha:1]
andHeight:rect.size.height];
y += SIZE_FONT_NAME+5;
float fromX = leftX;
float size = [UIScreen screenWidth]-leftX;
NSString *from = [NSString stringWithFormat:@"%@ %@", _data[@"time"], _data[@"from"]];
// 绘制 2015-05-25 三星GALAXY S4
[from drawInContext:context withPosition:CGPointMake(fromX, y) andFont:FontWithSize(SIZE_FONT_SUBTITLE)
andTextColor:[UIColor colorWithRed:178/255.0 green:178/255.0 blue:178/255.0 alpha:1]
andHeight:rect.size.height andWidth:size];
}
{
CGRect countRect = CGRectMake(0, rect.size.height-30, [UIScreen screenWidth], 30);
[[UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1] set];
CGContextFillRect(context, countRect);
float alpha = 1;
float x = [UIScreen screenWidth]-SIZE_GAP_LEFT-10;
NSString *comments = _data[@"comments"];
if (comments) {
CGSize size = [comments sizeWithConstrainedToSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) fromFont:FontWithSize(SIZE_FONT_SUBTITLE) lineSpace:5];
x -= size.width;
// 绘制下面的三个按钮
[comments drawInContext:context withPosition:CGPointMake(x, 8+countRect.origin.y)
andFont:FontWithSize(12)
andTextColor:[UIColor colorWithRed:178/255.0 green:178/255.0 blue:178/255.0 alpha:1]
andHeight:rect.size.height];
[[UIImage imageNamed:@"t_comments.png"] drawInRect:CGRectMake(x-5, 10.5+countRect.origin.y, 10, 9) blendMode:kCGBlendModeNormal alpha:alpha];
commentsRect = CGRectMake(x-5, self.height-50, [UIScreen screenWidth]-x+5, 50);
x -= 20;
}
NSString *reposts = _data[@"reposts"];
if (reposts) {
CGSize size = [reposts sizeWithConstrainedToSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) fromFont:FontWithSize(SIZE_FONT_SUBTITLE) lineSpace:5];
x -= MAX(size.width, 5)+SIZE_GAP_BIG;
[reposts drawInContext:context withPosition:CGPointMake(x, 8+countRect.origin.y)
andFont:FontWithSize(12)
andTextColor:[UIColor colorWithRed:178/255.0 green:178/255.0 blue:178/255.0 alpha:1]
andHeight:rect.size.height];
[[UIImage imageNamed:@"t_repost.png"] drawInRect:CGRectMake(x-5, 11+countRect.origin.y, 10, 9) blendMode:kCGBlendModeNormal alpha:alpha];
repostsRect = CGRectMake(x-5, self.height-50, commentsRect.origin.x-x, 50);
x -= 20;
}
[@"•••" drawInContext:context
withPosition:CGPointMake(SIZE_GAP_LEFT, 8+countRect.origin.y)
andFont:FontWithSize(11)
andTextColor:[UIColor colorWithRed:178/255.0 green:178/255.0 blue:178/255.0 alpha:.5]
andHeight:rect.size.height];
if ([_data valueForKey:@"subData"]) {
[[UIColor colorWithRed:200/255.0 green:200/255.0 blue:200/255.0 alpha:1] set];
CGContextFillRect(context, CGRectMake(0, rect.size.height-30.5, rect.size.width, .5));
}
}
// 生成图片
UIImage *temp = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
if (flag==drawColorFlag) {
postBGView.frame = rect;
postBGView.image = nil;
// 设置图片
postBGView.image = temp;
}
});
});
}
4.不设置圆角
现在的图片UI都喜欢设置圆角,圆角是有点耗CPU,所以要想方法不设置圆角但是做成圆角的效果。记住,这不是绕口令。
// 头像
avatarView = [UIButton buttonWithType:UIButtonTypeCustom];//[[VVeboAvatarView alloc] initWithFrame:avatarRect];
avatarView.frame = CGRectMake(SIZE_GAP_LEFT, SIZE_GAP_TOP, SIZE_AVATAR, SIZE_AVATAR);
avatarView.backgroundColor = [UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1];
avatarView.hidden = NO;
avatarView.tag = NSIntegerMax;
avatarView.clipsToBounds = YES;
[self.contentView addSubview:avatarView];
// 头像的遮罩
cornerImage = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, SIZE_AVATAR+5, SIZE_AVATAR+5)];
cornerImage.center = avatarView.center;
cornerImage.image = [UIImage imageNamed:@"corner_circle@2x.png"];
cornerImage.tag = NSIntegerMax;
[self.contentView addSubview:cornerImage];
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
NSIndexPath *ip = [self.tableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];
NSIndexPath *cip = [[self.tableView indexPathsForVisibleRows] firstObject];
NSInteger row = ip.row;
// 提前加载
if (ip.section == self.tableView.numberOfSections - 1 && cip.section == self.tableView.numberOfSections - 1 && cip.row < row) {
if (row >= self.dataSource.count-5) {
[self loadMoreListData];
}
}
}
优化TableView的性能可能是App里最重要的地方,因为TableView用到的地方太多了。如果大家还有什么好看法,记得联系我。。。。。