UITbaleView優(yōu)化方案

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的方式。

  1. 使用Autolayout進(jìn)行UI布局約束(要求cell.contentView的四條邊都與內(nèi)部元素有約束關(guān)系)矫钓。
  2. 指定TableView的estimatedRowHeight屬性的默認(rèn)值
  3. 指定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)化

  1. 使用貝塞爾曲線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];
  1. 使用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;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惋鹅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子殉簸,更是在濱河造成了極大的恐慌闰集,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件般卑,死亡現(xiàn)場離奇詭異武鲁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蝠检,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門沐鼠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事饲梭〕烁牵” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵憔涉,是天一觀的道長订框。 經(jīng)常有香客問我,道長兜叨,這世上最難降的妖魔是什么布蔗? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮浪腐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘顿乒。我一直安慰自己议街,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布璧榄。 她就那樣靜靜地躺著特漩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骨杂。 梳的紋絲不亂的頭發(fā)上涂身,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機(jī)與錄音搓蚪,去河邊找鬼蛤售。 笑死,一個胖子當(dāng)著我的面吹牛妒潭,可吹牛的內(nèi)容都是我干的悴能。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雳灾,長吁一口氣:“原來是場噩夢啊……” “哼漠酿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谎亩,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤炒嘲,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后匈庭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夫凸,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年嚎花,在試婚紗的時候發(fā)現(xiàn)自己被綠了寸痢。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡紊选,死狀恐怖啼止,靈堂內(nèi)的尸體忽然破棺而出道逗,到底是詐尸還是另有隱情,我是刑警寧澤献烦,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布滓窍,位于F島的核電站,受9級特大地震影響巩那,放射性物質(zhì)發(fā)生泄漏吏夯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一即横、第九天 我趴在偏房一處隱蔽的房頂上張望噪生。 院中可真熱鬧,春花似錦东囚、人聲如沸跺嗽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽桨嫁。三九已至,卻和暖如春份帐,著一層夾襖步出監(jiān)牢的瞬間璃吧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工废境, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留畜挨,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓彬坏,卻偏偏與公主長得像朦促,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子栓始,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容