iOS中最常用的控件UITableView枫浙,在項目中大量的使用刨肃,再次總結(jié)一下UITableView性能優(yōu)化的一些點(diǎn),也是平時寫代碼時的需要注意和使用的點(diǎn)箩帚。
cell的復(fù)用
cell的復(fù)用是tableview中最基本的提高性能的使用方法真友。有兩種書寫方式。
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Identifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
//cell=[[[NSBundle mainBundle]loadNibNamed:@“myCell" owner:self options:nil]lastObject];
}
return cell;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *Identifier = @"myCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID forIndexPath:indexPath];
return cell;
}
[self.tableView registerClass:[xxxxCell class] forCellReuseIdentifier:@"myCell"];
//[tableView registerNib:[UINib nibWithNibName:@"xxxxViewCell" bundle:nil] forCellReuseIdentifier:@"myCell"];
這兩種方式都可以實(shí)現(xiàn)cell的復(fù)用紧帕。但很多開發(fā)者會直接在cellForRowAtIndexPath將model數(shù)據(jù)直接綁定到cell中盔然。其實(shí)調(diào)用cellForRowAtIndexPath的時候cell并沒有顯示。因此為了提高效率是嗜,應(yīng)該吧數(shù)據(jù)綁定的操作寫在tableView:willDisplayCell:forRowAtIndexPath:方法中愈案。但是需要注意的是 willDisplayCell在cell 在tableview展示之前就會調(diào)用,此時cell實(shí)例已經(jīng)生成叠纷,所以不能更改cell的結(jié)構(gòu)刻帚,只能是改動cell上的UI的一些屬性(例如label的內(nèi)容等)。
cell的高度
在開發(fā)中一般會有兩種cell涩嚣。一種是定高的cell崇众。采用下面的方式即可設(shè)置cell的高度。
self.tableView.rowHeight = 100;
另外一種則是動態(tài)高度航厚。有時候一些圖片和文本內(nèi)容的顯示會出現(xiàn)cell的動態(tài)高度顷歌。此時需要計算每一個cell的高度,根據(jù)內(nèi)容的不同去設(shè)置每一個cell不同的行高幔睬,則通過下面的代理方法來返回cell的高度眯漩。
-(CGFloat)tableView:(UITableView*)tableViewheightForRowAtIndexPath:(NSIndexPath*)indexPath{
//根據(jù)cell的內(nèi)容計算高度
//return xxx;
}
每一次cell復(fù)用的時候都會重新計算一次,因此為了提高效率麻顶,可以將已經(jīng)計算好的cell的高度保存起來赦抖,等再次顯示的時候直接取出,節(jié)省了計算的時間辅肾。
還有通過Autolayout進(jìn)行布局队萤,使用self-sizing cell的方式。
- 使用Autolayout進(jìn)行UI布局約束(要求cell.contentView的四條邊都與內(nèi)部元素有約束關(guān)系)矫钓。
- 指定TableView的estimatedRowHeight屬性的默認(rèn)值
- 指定TableView的rowHeight屬性為UITableViewAutomaticDimension要尔。
- (void)viewDidload {
self.myTableView.estimatedRowHeight = 44.0;
self.myTableView.rowHeight = UITableViewAutomaticDimension;
}
異步化UI,不要阻塞主線程
像網(wǎng)絡(luò)圖片的加載新娜,使用異步加載赵辕。使用SDWebImage或者YYImage等第三方庫即可快速實(shí)現(xiàn)。將一些耗時操作放入到子線程中進(jìn)行處理概龄。比如CoreGraphics等進(jìn)行繪制圖表或者一些圖形还惠,可以進(jìn)行異步繪制,然后再回到主線程中顯示到UI上私杜。
合理使用hidden吸重。減少clearcolor的使用互拾。
減少視圖的數(shù)目
我們在cell上添加系統(tǒng)控件的時候,實(shí)際上系統(tǒng)都會調(diào)用底層的接口進(jìn)行繪制嚎幸,大量添加控件時,會消耗很大的資源并且也會影響渲染的性能寄猩。當(dāng)使用默認(rèn)的UITableViewCell并且在它的ContentView上面添加控件時會相當(dāng)消耗性能嫉晶。所以目前最佳的方法還是繼承UITableViewCell,并重寫drawRect方法田篇。
減少多余的繪制操作
在實(shí)現(xiàn)drawRect方法的時候替废,它的參數(shù)rect就是我們需要繪制的區(qū)域,在rect范圍之外的區(qū)域我們不需要進(jìn)行繪制泊柬,否則會消耗相當(dāng)大的資源椎镣。
不要給cell動態(tài)添加subview
在初始化cell的時候就將所有需要展示的添加完畢,然后根據(jù)需要來設(shè)置hide屬性顯示和隱藏兽赁。這樣可以有效的減少cell再復(fù)用的時候?qū)討B(tài)添加的控件進(jìn)行的操作耗時状答,提高流暢度。
按需加載
對于快速滑動的tableView刀崖,可以考慮只繪制快速滑動即將停止附件的那些cell惊科。對于活動中曇花一現(xiàn)的cell,沒必要繪制亮钦。不過這可能導(dǎo)致出現(xiàn)空白的cell馆截。
這里先回顧一下uiscrollView的滑動時代理的調(diào)用順序。
1蜂莉、-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
//開始拖動 UIscrollview 的時候被調(diào)用
2蜡娶、-(void)scrollViewDidScroll:(UIScrollView *)scrollView
//只要contentOffset 發(fā)生變化該(拖動、代碼設(shè)置)方法就會被調(diào)用映穗,反過來也可以用于監(jiān)控 contentOffset 的變化窖张。
3、-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(CGPoint *)targetContentOffset
//方法中 velocity 為 CGPointZero時(結(jié)束拖動時兩個方向都沒有速度)男公,沒有初速度荤堪,所以也沒有減速過程,willBeginDecelerating 和該didEndDecelerating 也就不會被調(diào)用如果 velocity 不為 CGPointZero 時枢赔,scrollview 會以velocity 為初速度澄阳,減速直到 targetContentOffset,也就是說在你手指離開屏幕的那一刻踏拜,就計算好了停留在那個位置的坐標(biāo)
4碎赢、-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
//用戶結(jié)束拖動后被調(diào)用,decelerate 為 YES 時速梗,結(jié)束拖動后會有減速過程肮塞。
5襟齿、-(void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
//減速動畫開始前被調(diào)用
6、- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
//減速動畫結(jié)束時被調(diào)用枕赵,可以用于判斷scrollview滑動是否停止猜欺。
方法3中從你滑動tableview時,手指離開的一瞬間其實(shí)也就已經(jīng)計算好了要停留的位置拷窜。此時你只加載顯示停留位置指定的前后幾行开皿。這樣也能提高效率,但在滑動過程中不可避免的會出現(xiàn)空白cell顯示的情況篮昧。
//按需加載 - 如果目標(biāo)行與當(dāng)前行相差超過指定行數(shù)赋荆,只在目標(biāo)滾動范圍的前后指定3行加載。 當(dāng) velocity 不為 CGPointZero 時懊昨,scroll view 會以 velocity 為初速度窄潭,減速直到 targetContentOffset
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
//即將滑動停留后展示的indexPath
NSIndexPath *ip = [self indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];
//如果滑動過快,這里是之前的值
NSIndexPath *cip = [[self indexPathsForVisibleRows] firstObject];
NSLog(@"row1 = %zi , row2 = %zi,velocity=%f",ip.row,cip.row,velocity.y);
NSInteger skipCount = 8; //以這個值作為開啟滑動區(qū)域展示
if (labs(cip.row-ip.row)>skipCount) {
NSArray *temp = [self indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, self.width, self.height)];
NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];
if (velocity.y<0) { //多加載后面3個cell酵颁,向下滑動
NSIndexPath *indexPath = [temp lastObject];
if (indexPath.row+3<datas.count) {
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:0]];
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row+2 inSection:0]];
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row+3 inSection:0]];
}
} else { //多加載上面3個cell
NSIndexPath *indexPath = [temp firstObject];
if (indexPath.row>2) {
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]];
}
if (indexPath.row>1) {
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]];
}
if (indexPath.row>0) {
[arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]];
}
}
//得到快速滑動即將停止時候需顯示的所有cell
[needLoadArr addObjectsFromArray:arr];
}
}
離屏渲染
- 為圖層設(shè)置遮罩(layer.mask)
- 將圖層的layer.masksToBounds / view.clipsToBounds屬性設(shè)置為true
- 將圖層layer.allowsGroupOpacity屬性設(shè)置為YES和layer.opacity小于1.0
- 為圖層設(shè)置陰影(layer.shadow *)嫉你。
- 為圖層設(shè)置layer.shouldRasterize=true
- 具有l(wèi)ayer.cornerRadius,layer.edgeAntialiasingMask材义,layer.allowsEdgeAntialiasing的圖層
- 文本(任何種類均抽,包括UILabel,CATextLayer其掂,Core Text等)油挥。
- 使用CGContext在drawRect :方法中繪制大部分情況下會導(dǎo)致離屏渲染,甚至僅僅是一個空的實(shí)現(xiàn)
圓角的優(yōu)化
- 使用貝塞爾曲線UIBezierPath和Core Graphics框架畫出一個圓角
UIImageView imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"myImg"];
//開始對imageView進(jìn)行畫圖
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
//使用貝塞爾曲線畫出一個圓形圖
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
//結(jié)束畫圖
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
- 使用CAShapeLayer和UIBezierPath設(shè)置圓角
UIImageView *imageView=[[UIImageView alloc]initWithFrame:CGRectMake(100,100,100,100)];
imageView.image=[UIImage imageNamed:@"myImg"];
UIBezierPath *maskPath=[UIBezierPath bezierPathWithRoundedRect:imageView.boundsbyRoundingCorners:UIRectCornerAllCornerscornerRadii:imageView.bounds.size];
CAShapeLayer *maskLayer=[[CAShapeLayer alloc]init];
//設(shè)置大小
maskLayer.frame=imageView.bounds;
//設(shè)置圖形樣子
maskLayer.path=maskPath.CGPath;
imageView.layer.mask=maskLayer;
[self.view addSubview:imageView];
shadow優(yōu)化
對于shadow款熬,如果圖層是個簡單的幾何圖形或者圓角圖形深寥,我們可以通過設(shè)置shadowPath來優(yōu)化性能,能大幅提高性能贤牛。示例如下:
imageView.layer.shadowColor=[UIColor grayColor].CGColor;
imageView.layer.shadowOpacity=1.0;
imageView.layer.shadowRadius=2.0;
UIBezierPath *path=[UIBezierPath bezierPathWithRect:imageView.frame];
imageView.layer.shadowPath=path.CGPath;