UITableView深度優(yōu)化

UITableView的核心思想是:cell的重用機(jī)制。UITbleView只會(huì)創(chuàng)建一屏幕(或一屏幕多一點(diǎn))的cell, 每當(dāng)cell滑出屏幕時(shí),就會(huì)放倒一個(gè)集合(或數(shù)組)中(這里相當(dāng)于一個(gè)重用池),當(dāng)要顯示某一個(gè)位置的cell時(shí),會(huì)先根據(jù)ReuseIdentifier去集合和數(shù)組中去取,如果有直接拿來用,如果沒有的話,才會(huì)去創(chuàng)建,這樣極大地減少了內(nèi)存的開銷。

UITableView的兩個(gè)重要的回調(diào)方法是:tableView:cellForRowAtIndexPath:和tableView:heightForRowAtIndexPath:。由于UITableView是繼承自UIScrollView的,需要先確定它的contentSize及每個(gè)Cell的位置,然后才會(huì)把重用的cell放置到對應(yīng)的位置。UITableView的回調(diào)順序是先多次調(diào)用tableView:heightForRowAtIndexPath:來確定cell的位置,然后才會(huì)調(diào)用tableView:cellForRowAtIndexPath:從而來顯示在當(dāng)前的屏幕的cell。

舉個(gè)例子來說:如果現(xiàn)在要顯示100個(gè)Cell,當(dāng)前屏幕顯示5個(gè)。那么刷新(reload)UITableView時(shí),UITableView會(huì)先調(diào)用100次tableView:heightForRowAtIndexPath:方法,然后調(diào)用5次tableView:cellForRowAtIndexPath:方法;滾動(dòng)屏幕時(shí),每當(dāng)Cell滾入屏幕,都會(huì)調(diào)用一次tableView:heightForRowAtIndexPath:、tableView:cellForRowAtIndexPath:方法。

1:把賦值和計(jì)算布局分離。這樣讓tableView:cellForRowAtIndexPath:只負(fù)責(zé)賦值,tableView:heightForRowAtIndexPath:只負(fù)責(zé)計(jì)算高度。注意,這兩個(gè)方法各司其職。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

NSDictionary?*dict?=?self.dataList[indexPath.row];

return[ContacterTableCell?cellHeightOfInfo:dict];

}

基于上面的思路,我們可以在獲得數(shù)據(jù)后,直接根據(jù)數(shù)據(jù)源計(jì)算出對應(yīng)的布局,并緩存到數(shù)據(jù)源中,這樣在tableView:heightForRowAtIndexPath:方法中就直接返回高度,而不需要每次都計(jì)算了。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

NSDictionary?*dict?=?self.dataList[indexPath.row];

CGRect?rect?=?[dict[@"frame"]?CGRectValue];

returnrect.frame.height;

}

2:上面的方案并不是最佳的方案,可以滿足簡單的界面!如果像朋友圈那樣的圖文混排,這種方案其實(shí)扛不住的,自定義cell的繪制。在cell上添加系統(tǒng)的控件時(shí),實(shí)質(zhì)系統(tǒng)都需要調(diào)用底層的接口進(jìn)行繪制,如果大量的添加,對資源的開銷會(huì)很大,所以我們可以直接繪制,提高效率。

首先自定義cell的draw方法,(也可以重寫drawRect)然后在方法中實(shí)現(xiàn)

//異步繪制

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{

CGRect?rect?=?[_data[@"frame"]?CGRectValue];

UIGraphicsBeginImageContextWithOptions(rect.size,?YES,?0);//<CoreGraphics/CoreGraphics.h>

CGContextRef?context?=?UIGraphicsGetCurrentContext();

//整個(gè)內(nèi)容的背景

[[UIColor?colorWithRed:250/255.0?green:250/255.0?blue:250/255.0?alpha:1]?set];

CGContextFillRect(context,?rect);

//轉(zhuǎn)發(fā)內(nèi)容的背景

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];

//時(shí)間+設(shè)備

y?+=?SIZE_FONT_NAME+5;

float?fromX?=?leftX;

float?size?=?[UIScreen?screenWidth]-leftX;

NSString?*from?=?[NSString?stringWithFormat:@"%@??%@",?_data[@"time"],?_data[@"from"]];

[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];

}

//將繪制的內(nèi)容以圖片的形式返回,并調(diào)主線程顯示

UIImage?*temp?=?UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

dispatch_async(dispatch_get_main_queue(),?^{

if(flag==drawColorFlag)?{

postBGView.frame?=?rect;

postBGView.image?=?nil;

postBGView.image?=?temp;

}

}

//內(nèi)容如果是圖文混排,就添加View,用CoreText繪制

[self?drawText];

}}

大體的思路是這個(gè)情況,各個(gè)信息都是根據(jù)之前算好的布局進(jìn)行繪制的。這里是需要異步繪制,但如果在重寫 drawRect方法就不需要用GCD異步線程了,因?yàn)閐rawRext本來就是異步繪制的。

3:進(jìn)行異步繪制這樣的話,UITableView的效率提高了一個(gè)等級(jí)!不過我們還可以從UIScrollerView的角度出發(fā),再次找到突破口

滑動(dòng)UITableView時(shí)按需加載對應(yīng)的內(nèi)容:

//按需加載 - 如果目標(biāo)行與當(dāng)前行相差超過指定行數(shù),只在目標(biāo)滾動(dòng)范圍的前后指定3行加載。

-?(void)scrollViewWillEndDragging:(UIScrollView?*)scrollView?withVelocity:(CGPoint)velocity?targetContentOffset:(inout?CGPoint?*)targetContentOffset{

NSIndexPath?*ip?=?[self?indexPathForRowAtPoint:CGPointMake(0,?targetContentOffset->y)];

NSIndexPath?*cip?=?[[self?indexPathsForVisibleRows]?firstObject];

NSInteger?skipCount?=?8;

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)?{

NSIndexPath?*indexPath?=?[temp?lastObject];

if(indexPath.row+33)?{

[arr?addObject:[NSIndexPath?indexPathForRow:indexPath.row-3?inSection:0]];

[arr?addObject:[NSIndexPath?indexPathForRow:indexPath.row-2?inSection:0]];

[arr?addObject:[NSIndexPath?indexPathForRow:indexPath.row-1?inSection:0]];

}

}

[needLoadArr?addObjectsFromArray:arr];

}

}

記得在tableView:cellForRowAtIndexPath:方法中加入判斷:

if(needLoadArr.count>0&&[needLoadArr indexOfObject:indexPath]==NSNotFound) {

[cell?clear];

return;

}

滾動(dòng)很快時(shí),只加載目標(biāo)范圍內(nèi)的Cell,這樣按需加載,極大的提高流暢度。

總結(jié):UITableView的優(yōu)化主要從三個(gè)方面入手

(1)提前計(jì)算并緩存好高度,因?yàn)閔eightForRowAtIndexPath:是調(diào)用最頻繁的方法

(2)異步繪制,遇到復(fù)雜的界面,遇到性能瓶頸時(shí),可能就是突破點(diǎn)

(3)滑動(dòng)時(shí)按需加載,這個(gè)在大量的圖片展示,網(wǎng)絡(luò)加載的時(shí)候很管用!

除了以上最主要的三個(gè)方面,還有其他的優(yōu)化點(diǎn):

?正確使用reuseIdentifier來重用cells

?盡量使所有的view opaque,包括cell自身。

opaque屬性提示繪制系統(tǒng)如何處理view。如果opaque設(shè)置為YES,繪圖系統(tǒng)會(huì)將view看為完全不透明,這樣會(huì)繪圖系統(tǒng)就可以優(yōu)化一些繪制操作以提升性能。如果設(shè)置為NO,那么繪圖系統(tǒng)結(jié)合其它的內(nèi)容來處理view。默認(rèn)情況下,這個(gè)屬性是YES.

?盡量少用或不用透明圖層

?如果cell內(nèi)的顯示的內(nèi)容來自web,使用異步加載,緩存請求結(jié)果

?減少subviews的數(shù)量

?在heightForRowAtIndexPath:中盡量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后緩存結(jié)果

?盡量少用addView給Cell動(dòng)態(tài)添加View,可以初始化時(shí)就添加,然后通過hide來控制是否顯示

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容