標籤(空格分隔): UITableView UITableViewCell UITableView性能優化 UITableView性能優化精講面試
參考文章編程
摘要:UITableView是iOS開發中很是重要的控件之一,它可以展現多行數據,支持滾動.在大部分APP中都佔有很大的比重.那麼有關UITableView的性能優化也顯得尤其重要,本文後面也將着重講這個。數組
##UITableView的簡單使用 ###一、UITableView的建立緩存
代碼方式性能優化
//tableView的建立必須制定一個樣式,樣式在初始化以後不能修改
//tableView分兩種風格,Plain:不分區,grouped:分區
_tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain];
_tableView.delegate = self;
_tableView.dataSource = self;
//告訴tableView,它上面的cell是根據UItableViewCell建立的,若是重用的cell找不到,系統會根據這個類來建立一個cell,不須要編程人員建立
//解釋2://給tableView的某個重用標示符註冊一個類,當tableView找這個重用標示符的cell,若是找不到,就會自動建立這個類的對象,(始終能找到可重用的cell)
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
[self.view addSubview:_tableView];
複製代碼
xib方式 在設計界面拖入,並在屬性檢查器中設置各類屬性bash
###二、UITableView的屬性網絡
// 設置每一行cell的高度
self.tableView.rowHeight = 80;
// 設置每一組的頭部標題高度
self.tableView.sectionHeaderHeight = 50;
// 設置每一組的尾部標題高度
self.tableView.sectionFooterHeight = 50;
// 設置分割線的顏色
self.tableView.separatorColor = [UIColor redColor];
// 設置分割線的樣式
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
// 設置表頭控件---這裏主要應用是打廣告
self.tableView.tableHeaderView = [[UISwitch alloc] init];
// 設置表尾控件---這裏主要應用是加載數據
self.tableView.tableFooterView = [[UISwitch alloc] init];
// 設置索引條的文字顏色
self.tableView.sectionIndexColor = [UIColor orangeColor];
// 設置索引條的背景顏色
self.tableView.sectionIndexBackgroundColor = [UIColor yellowColor];
複製代碼
###三、隱藏TableView分割線的方法app
// 方法一:設置分割線的顏色
self.tableView.separatorColor = [UIColor clearColor];
// 方法二:設置分割線的樣式
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
複製代碼
###四、UITableViewCell的屬性佈局
//cell中默認包含的空間
cell.imageView
cell.textLabel
cell.detaliTextLabel
cell.contentView
// 設置右邊顯示的指示控件
// accessoryView的優先級 > accessoryType
cell.accessoryView = [[UISwitch alloc] init];
// 設置右邊顯示的指示樣式
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// 設置cell選中的樣式
// 設置cell的選中樣式無
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// 下面三種方式在iOS7以後,表現形式同樣了,都是灰色
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
複製代碼
###五、設置cell的背景顏色性能
// 整體效果爲大背景藍色,字體背景爲紅色,下面代碼能夠調換順序,效果同樣
cell.backgroundColor = [UIColor redColor];
UIView *bg = [[UIView alloc] init];
bg.backgroundColor = [UIColor blueColor];
cell.backgroundView = bg;
複製代碼
###六、設置cell選中的背景View
// 設置cell選中的背景view
UIView *seletedBg = [[UIView alloc] init];
seletedBg.backgroundColor = [UIColor yellowColor];
cell.selectedBackgroundView = seletedBg;
複製代碼
###七、兩個協議
tablewView代理方法的執行順序。 UITableView返回多少組----->每組返回多少行cell--->計算每一個cell的高度---->指定cell佈局
UITableViewDelegate
/**
* 1.當用戶點擊(選中)某一行cell就會調用這個方法
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"選中了:%zd",indexPath.row);
}
/**
* 2.當用戶取消點擊(選中)某一行cell就會調用這個方法
*/
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"取消選中了:%zd",indexPath.row);
}
/**
* 3.返回每一組的頭部高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
}
/**
* 4.返回每一組的尾部高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
}
/**
* 5.返回每一行cell的高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row % 2 == 0) {
return 50;
} else {
return 100;
}
}
/**
* 6.返回每一組的頭部控件
*/
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
return [UIButton buttonWithType:UIButtonTypeContactAdd];
}
/**
* 7.返回每一組的尾部控件
*/
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
return [UIButton buttonWithType:UIButtonTypeContactAdd];
}
/*
* 8.設置每一行的編輯樣式:當表處於編輯狀態時,默認的編輯樣式爲刪除
*/
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
//編輯狀態爲刪除
return UITableViewCellEditingStyleDelete;
//編輯狀態爲插入
//return UITableViewCellEditingStyleInsert;
}
//經過edditing的值來顯示編輯狀態
_tableView.editing = !_tableView.editing;
/*
* 9.表提交編輯狀態的時候會調用這個方法
*/
-(void)tableView:(UITableView *)tableView commitEditingStyle: (UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[_dataArray removeObjectAtIndex:indexPath.row];
[_tableView reloadData];
}
/*
* 10.設置編輯中刪除的文字
*/
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
return @"走你";
}
/*
* 11.右側按鈕被點擊時會調用該方法
*/
-(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"調用了cell右側按鈕的方法");
}
複製代碼
###八、兩個協議二:UITableViewDataSources
/**
* 告訴tableView一共有多少組數據
*/
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
/**
* 告訴tableView第section組有多少行
*/
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
/**
* 告訴tableView每一行顯示的內容(tableView每一行的內容是是第一個UITableViewCell)
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
/**
* 告訴tableView每一組的頭部標題
*/
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
/**
* 告訴tableView每一組的尾部標題
*/
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
/**
* 返回索引條的文字
*/
- (NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
// NSMutableArray *titles = [NSMutableArray array];
// for (XMGCarGroup *group in self.carGoups) {
// [titles addObject:group.title];
// }
// return titles;
// 抽取self.carGoups這個數組中每個元素(XMGCarGroup對象)的title屬性的值,放在一個新的數組中返回
return [self.carGoups valueForKeyPath:@"title"];
複製代碼
##2、UITableViewCell 在UITableView中,每個單元格被稱爲cell,若是想在UITableView中顯示數據,須要設置UITableView中cell的數量及每一個cell顯示的內容.UITableView並不能直接顯示數據,它須要設置數據源(datasource),數據源遵照協議,並實現其中對應的方法設置數據及內容便可. ###UITableViewCell的建立
設置Cell的三個屬性
cell.imageView //cell左邊的圖片
cell.textLabel
cell.detailTextLabel
複製代碼
經過自定義類 建立一個累繼承於UITableViewCell,添加以下屬性
@property (nonatomic,retain)UIImageView *headerImageView;
@property (nonatomic,retain)UILabel *nameLabel;
@property (nonatomic,retain)UILabel *timeLabel;
@property (nonatomic,retain)UILabel *messageLabel;
@property (nonatomic,retain)UIImageView *picImageView;
複製代碼
.m文件中的實現
//若是是alloc建立的cell,各個自定義屬性空間的初始化代碼寫在init方法中
(instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
//頭像
_headerImageView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 8, 60, 60)];
_headerImageView.layer.cornerRadius = 30;
_headerImageView.layer.masksToBounds = YES;
[self.contentView addSubview:_headerImageView];
//名字
_nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(76, 8, 240, 20)];
_nameLabel.font = [UIFont boldSystemFontOfSize:16];
_nameLabel.textColor = [UIColor redColor];
[self.contentView addSubview:_nameLabel];
//發佈時間
_timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(76, 36, 240, 20)];
_timeLabel.font = [UIFont systemFontOfSize:12];
[self.contentView addSubview:_timeLabel];
//動態正文
_messageLabel = [[UILabel alloc] initWithFrame:CGRectMake(8, 76, 304, 60)];
_messageLabel.font = [UIFont systemFontOfSize:16];
//numberOfLines行數,設置爲0不限制行數。
_messageLabel.numberOfLines = 0;
[self.contentView addSubview:_messageLabel];
//動態配圖
_picImageView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 144, 100,100)];
[self.contentView addSubview:_picImageView];
}
return self;
}
複製代碼
經過xib自定義 首先建立一個帶xib的UITableViewCell,拖控件設置cell高度。拉線生成屬性;
自定義類中
//若是cell是從xib中加載的,那麼不走init方法,因此初始化代碼不能寫在init方法中
//當自身從xib中加載的時候調用,因此自定義xib的代碼應該在這裏實現
(void)awakeFromNib {
[super awakeFromNib];
// Initialization code
_headerImageView.layer.cornerRadius = 30;
_headerImageView.layer.masksToBounds = YES;
}
複製代碼
註冊重用
```
//給TableView的某個重用標示符註冊一個xib,當tableView找這個標識符時,若是找不 到,就會自動從註冊的西邊中區加載一個cell。
[_tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:nil] forCellReuseIdentifier:@"cell"];
```
複製代碼
##3、性能優化
1)儘可能使用cell的複用。
使用cell的複用,能夠減小內存的開銷,沒有開闢新的空間,也減小了一些計算量。
複用原理:當滾動列表時(UITableView)部分cell會移除Window 可是移除的cell並無被當即釋放 而是放到了一個叫作複用池的對象池中,處於待定狀態,當有新的cell要出如今Window界面上時,首先會從複用池中尋找是否有相同類型的cell,若是有直接拿過用(最直觀的表現是新出現的cell有沒有開闢新的內存空間),若是沒有,建立一個新的類型的cell,因此UITableView可能擁有多種類型的cell,複用池也可能存儲着多種類型的cell,系統經過定義reuseIndentifer做爲每一個cell的惟一標示符來肯定即將出現的cell複用何種類型的cell。
2)對於不定高的cell 提早將每一個cell的高度存入數組,出現一個cell的時候,直接從數組中拿出確切的高度便可,不用臨時計算cell的高度。對於固定高的cell和不定高的cell一樣適用。一樣也能夠在存儲在model中,在獲取數據後要賦值給model時進行計算。
3)涉及網絡請求加載數據在UITableView滑動結束的時候在進行加載數據(渲染)避免卡頓。
一、UITableView繼承自UIScrollView,繼承了後者的方法。
//滑動結束的方法
- (void)scrollViewDidEndDragging:(UIScrollView*)scrollView willDecelerate:(BOOL)decelerate
//減速結束以後的方法
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView
二、在tableView必須實現的二個方法中(加載cell的方法中)將數據的渲染寫在如下if語句中
if(self.tableView.dragging==NO&&self.tableView.decelerating==NO)
複製代碼
4)對於tableView的自定義控件 尤爲是UIImageView,儘可能減小使用圓角,陰影等layer屬性,儘可能減小使用alpha(透明度)來設置透明度,(在項目開發中,讓UI設計師設計原圖就是帶圓角的圖) 陰影,圓角這些layer效果都是在GPU中完成的。
一、當多個視圖重疊時,一個像素同時屬於不少subviews,GPU會進行合成渲染,須要渲染一個像素兩次或屢次,而渲染的最慢的操做就是混合。所以當視圖結構太過複雜,就會大量消耗GPU的資源。因此當一個空間自己是不透明,注意設定alpha爲1,這樣能夠避免無用的alpha通道合成,下降GPU的負載。 另外在layer層渲染圖層時會涉及上下文切換以及離屏渲染之類的,系統開銷會很大,特別是在cell視圖很複雜的時候,因爲渲染問題致使的內存開銷會讓你的tableview很是卡頓。好比cell中須要設置頭像圓形直接設置圓角會很卡,那麼咱們能夠用Quartz2d把拿到的圖片處理一遍在給cell使用。
二、對控件設置cornerRadius後對其進行clip或mask操做時 會致使offscreenrendering這個也是在GPU中進行的 若是在滑動時 圓角對象太多 回到GPU的負載大增幅。
這時咱們能夠設置layer的shouldRasterize屬性爲YES,能夠將負載轉移給CPU,更完全的是直接使用帶圓角的原圖。
5)儘可能使用懶加載
懶加載又稱爲延遲加載,其實是重寫某個對象的getter方法 原理:程序一開始並不對它進行初始化 而是在用到他的時候 才爲他開闢內存供它使用。
好處:
一、沒必要將建立的對象的代碼所有寫在ViewDidLoad中,代碼可讀性強。 二、每一個控件的getter方法,分別負責各自的實例化處理,代碼彼此之間獨立性強 鬆耦合。
6)減小返回給的cell裏面的處理邏輯和處理時間。
以驢媽媽爲例:各個UI控件整合到一塊兒,實際上只有一個控件。
7)設置每一個cell的opaque屬性 ----面試亮點
opaque意思是不透明的 渾濁的 有YES和NO二個結果,若是控件自己不透明,咱們設置opaque爲YES。
opaque爲YES表示告訴iOS當前視圖背後沒有須要繪製的內容,同時容許iOS的繪圖方法經過一些優化來加速當前視圖的繪製。
爲何咱們設置Alpha的值爲1的時候仍然要設置opaque的屬性爲YES? Alpha屬性只對當前須要繪製的視圖起做用,若是當前視圖並無填滿父視圖或者當前視圖上存在一些洞(由Alpha通道所致),那麼圖像視圖下方的內容將仍然可見,無論Alpha的值是多少。選中就是讓iOS明白不須要爲圖像視圖以後的東西浪費繪製時間。
如下是官方描述
default is YES. opaque views must fill their entire bounds or the results are undefined. the active CGContext in drawRect: will not have been cleared and may have non-zeroed pixels
8)分段加載數據
設置分頁加載數據,也就是上拉刷新和下拉加載。
如下是cell簡單複用代碼
#import "ViewController.h"
#import "XMGTestCell.h"
@interface ViewController ()
@end
@implementation ViewController
NSString *ID = @"wine";
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.rowHeight = 100;
// 註冊 ID這個標識 對應的 cell類型 爲UITableViewCell這種類型
[self.tableView registerClass:[XMGTestCell class] forCellReuseIdentifier:ID];
}
#pragma mark - 數據源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 200;
}
/**
* 每當一個cell進入視野範圍內就會調用1次
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.去緩存池取可循環利用的cell
XMGTestCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
// 2.設置數據
cell.textLabel.text = [NSString stringWithFormat:@"第%zd行數據",indexPath.row];
return cell;
}
複製代碼