如果問iOS中最重要的最常用的UI控件是什么,我覺得UITableView當(dāng)之無愧!似乎所有常規(guī)APP都使用到了UITableView。下面就講一講UITableView的常用知識(shí)和使用原理及性能優(yōu)化!
1.簡介
- UITableView故名思議是一種表格控件,是iOS應(yīng)用中常用的表格控件。常見UITableView如圖:
UITableView繼承于UIScrollview,因此它默認(rèn)支持垂直滾動(dòng)(只支持垂直滾動(dòng))
UITableView性能極佳,它可以出色的完成我們工作中的很多功能。
UITableView一共有兩種樣式:UITableViewStylePlain 和 UITableViewStyleGroup
效果分別如圖:
UITableViewStylePlain:一種平鋪的效果,并且分組組頭默認(rèn)有停靠效果;
UITableViewStyleGroup:一種組間分離的效果,每組之間間距較明顯,有明顯分組跡象。
2.數(shù)據(jù)源
說完了UITableView的簡介,下面來說說他它是如何展示數(shù)據(jù)的。
- UITableView要展示數(shù)據(jù)必須設(shè)置數(shù)據(jù)源,沒有數(shù)據(jù)源(DataSource)它就是一個(gè)空殼。
- UITableView會(huì)調(diào)用數(shù)據(jù)源方法來查詢一共有多少行數(shù)據(jù)以及當(dāng)前顯示什么樣的數(shù)據(jù)。
- 凡是遵守 UITableViewDelegate的OC對象都可以做UITableView的數(shù)據(jù)源
UITableView和數(shù)據(jù)源的關(guān)系如下
數(shù)據(jù)源方法的調(diào)用過程
-
調(diào)用如下方法,查詢一共有多少組數(shù)據(jù)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
-
調(diào)用如下方法,查詢每一組一共有多少行數(shù)據(jù)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
-
調(diào)用如下方法,查詢每一行顯示什么樣的內(nèi)容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
UITableView還有一些其他的數(shù)據(jù)源方法如下
/**
* 返回組頭標(biāo)題
*/
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
/**
* 返回尾標(biāo)題
*/
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
···
3.UITableViewCell的復(fù)用機(jī)制
3.1. 返回UITableViewCell的數(shù)據(jù)源方法的調(diào)用
最簡單的返回UITableViewCell的方法如下
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];
return cell;
}
但是實(shí)際使用過程中這樣是非常耗費(fèi)性能的,因?yàn)楫?dāng)用戶滑動(dòng)UITableView的時(shí)候,上面方法會(huì)瘋狂的調(diào)用,瘋狂的創(chuàng)建UITableViewCell。
蘋果對這種情況做了一些性能優(yōu)化,UITableViewCell在每次創(chuàng)建的時(shí)候只會(huì)創(chuàng)建當(dāng)前UI頁面上可見的Cell。當(dāng)Cell滑動(dòng)到屏幕外面,當(dāng)前Cell會(huì)被放入到緩存池中,并且可以給Cell設(shè)置一個(gè)復(fù)用標(biāo)識(shí),當(dāng)下次使用Cell的時(shí)候可以先到緩存池中查找,如果有對應(yīng)復(fù)用標(biāo)識(shí)的Cell就直接拿出來使用,并重新給內(nèi)容賦值。
所以根據(jù)蘋果復(fù)用Cell的思路我們在調(diào)用返回cell的方法思路應(yīng)該如下:
- 1.先從緩存池中查找看有沒有可以復(fù)用的cell
- 2.如果有就直接用,如果沒有在根據(jù) 復(fù)用標(biāo)識(shí) 創(chuàng)建
- 3.創(chuàng)建完成之后對原來數(shù)據(jù)進(jìn)行覆蓋(防止復(fù)用的cell展示異常數(shù)據(jù))
所以上面方法從提高性能角度考慮,應(yīng)該重新寫為下面這樣:
3.2. UITableView性能優(yōu)化--Cell復(fù)用方式1
/**
* 什么時(shí)候調(diào)用:每當(dāng)有一個(gè)cell進(jìn)入視野范圍的時(shí)候調(diào)用
*/
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 0.創(chuàng)建cell復(fù)用標(biāo)識(shí)
static NSString *reuseIdentifier = @"cell";
// 1.根據(jù)復(fù)用標(biāo)識(shí)在復(fù)用池中進(jìn)行查找
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// 2.判斷如果復(fù)用池cell為nil就根據(jù)復(fù)用標(biāo)識(shí)自己創(chuàng)建
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
}
// 3.拿到cell進(jìn)行數(shù)據(jù)覆蓋
cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];
return cell;
}
3.2. UITableView性能優(yōu)化--Cell復(fù)用方式2
我們可以手動(dòng)給tableview注冊對應(yīng)標(biāo)識(shí)的cell,保證從緩存池中能加載到對應(yīng)標(biāo)識(shí)的cell。
通過代碼注冊
// 注冊cell
static NSString *reuseIdentifier = @"cell";
[tableview registerClass:[UITableViewCell class] forCellReuseIdentifier:reuseIdentifier];
通過Xib注冊
// 注冊cell
static NSString *reuseIdentifier = @"cell"; // 標(biāo)識(shí)可以在Xib中創(chuàng)建
[tableview registerNib:[UINib nibWithNibName:@"對應(yīng)的Xib名稱" bundle:nil] forCellReuseIdentifier:reuseIdentifier];
在數(shù)據(jù)源方法中返回cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.根據(jù)復(fù)用標(biāo)識(shí)在復(fù)用池中進(jìn)行查找
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// 2.拿到cell進(jìn)行數(shù)據(jù)覆蓋
cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];
return cell;
}
3.3 UITableView性能優(yōu)化--Cell復(fù)用方式3
第三種方式是通過UITableViewController在StoryBoard中創(chuàng)建,見下面:5.UITableViewController
4.UITableView的代理方法
有了數(shù)據(jù)源方法,tableview就可以正常展示數(shù)據(jù)。有了對應(yīng)的代理方法就可以正常監(jiān)聽tableview的事件操作。
下面列舉一些常用代理方法:
/**
返回行高,可根據(jù)不同行返回不同高
@param tableView tableview
@param indexPath 位置
@return 行高
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
/**
返回估計(jì)行高,此方法不進(jìn)行真實(shí)計(jì)算,課改變Cell計(jì)算高度方法的調(diào)用順序,
@param tableView tableview
@param indexPath 位置
@return 行高
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;
/**
cell的點(diǎn)擊事件處理
@param tableView tableview
@param indexPath 位置
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
/**
cell將要被選中 -- 先需取消選中某個(gè)cell才能正常選中新的cell
*/
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
/**
cell將要被取消選中
*/
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath
···
5.UITableViewController
UITableViewController繼承自UIViewController。可以方便創(chuàng)建有tableview的控制器
UITableViewController默認(rèn)擁有tableview。在UITableViewController中 self.tableView 就是 self.view
UITableViewController默認(rèn)繼承 UITableViewDelegate和UITableViewDataSource,所有可以直接實(shí)現(xiàn)對應(yīng)的數(shù)據(jù)源方法和代理方法即可。
-
如果在StoryBoard中錯(cuò)誤將UIViewController當(dāng)做UITableViewController使用會(huì)報(bào):加載不到UITableview的錯(cuò)誤
Snip20170321_10.png -
在StoryBoard中創(chuàng)建UITableViewController并加載cell步驟如下:
拖一個(gè)UITableViewController出來
設(shè)置initial view controller(程序第一個(gè)加載項(xiàng))
修改對應(yīng)的類型為自己創(chuàng)建的VC(如果有)
-
設(shè)置Dynamic Prototypes動(dòng)態(tài)類型的內(nèi)容,下面數(shù)量至少為1
Snip20170321_11.png -
設(shè)置cell的復(fù)用標(biāo)識(shí)identfier Snip20170321_12.png
在代碼中直接加載對應(yīng)cell即可(無需判空/創(chuàng)建)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 1.會(huì)默認(rèn)去緩存池中進(jìn)行查找,如果沒有自動(dòng)去StoryBaord中找
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// 2.拿到cell進(jìn)行數(shù)據(jù)覆蓋
cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];
return cell;
}
```
**通過StoryBoard創(chuàng)建Cell更加方便**
小結(jié)
- UITableView是iOS開發(fā)中常用的UI控件
- UITableView需要實(shí)現(xiàn)數(shù)據(jù)源方法,來確定自己展示什么內(nèi)容,沒有數(shù)據(jù)源的UITableview只是一個(gè)空架子
- UITableView可以通過復(fù)用UITableViewCell來實(shí)現(xiàn)性能優(yōu)化
- Cell復(fù)用的方式有三種(如上)
- UITableViewController來實(shí)現(xiàn)帶有tableview的頁面更方便