iOS回顾笔记(07) -- UITableView的使用和性能优化

如果问iOS中最重要的最常用的UI控件是什么,我觉得UITableView当之无愧!似乎所有常规APP都使用到了UITableView。下面就讲一讲UITableView的常用知识和使用原理及性能优化!

1.简介

  • UITableView故名思议是一种表格控件,是iOS应用中常用的表格控件。常见UITableView如图:
Snip20170320_7.png
  • UITableView继承于UIScrollview,因此它默认支持垂直滚动(只支持垂直滚动)

  • UITableView性能极佳,它可以出色的完成我们工作中的很多功能。

  • UITableView一共有两种样式:UITableViewStylePlain 和 UITableViewStyleGroup

效果分别如图:

Snip20170320_8.png

UITableViewStylePlain:一种平铺的效果,并且分组组头默认有停靠效果;
UITableViewStyleGroup:一种组间分离的效果,每组之间间距较明显,有明显分组迹象。

2.数据源

说完了UITableView的简介,下面来说说他它是如何展示数据的。

  1. UITableView要展示数据必须设置数据源,没有数据源(DataSource)它就是一个空壳。
  2. UITableView会调用数据源方法来查询一共有多少行数据以及当前显示什么样的数据。
  3. 凡是遵守 UITableViewDelegate的OC对象都可以做UITableView的数据源

UITableView和数据源的关系如下

Snip20170320_9.png

数据源方法的调用过程

  1. 调用如下方法,查询一共有多少组数据

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
    
  2. 调用如下方法,查询每一组一共有多少行数据

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
    
  3. 调用如下方法,查询每一行显示什么样的内容

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
    

UITableView还有一些其他的数据源方法如下

/**
 *  返回组头标题
 */
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
/**
 *  返回尾标题
 */
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;

···

3.UITableViewCell的复用机制

3.1. 返回UITableViewCell的数据源方法的调用

最简单的返回UITableViewCell的方法如下

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [[UITableViewCell alloc] init];
    cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];
    
    return cell;
}

但是实际使用过程中这样是非常耗费性能的,因为当用户滑动UITableView的时候,上面方法会疯狂的调用,疯狂的创建UITableViewCell。

苹果对这种情况做了一些性能优化,UITableViewCell在每次创建的时候只会创建当前UI页面上可见的Cell。当Cell滑动到屏幕外面,当前Cell会被放入到缓存池中,并且可以给Cell设置一个复用标识,当下次使用Cell的时候可以先到缓存池中查找,如果有对应复用标识的Cell就直接拿出来使用,并重新给内容赋值。

所以根据苹果复用Cell的思路我们在调用返回cell的方法思路应该如下:

  • 1.先从缓存池中查找看有没有可以复用的cell
  • 2.如果有就直接用,如果没有在根据 复用标识 创建
  • 3.创建完成之后对原来数据进行覆盖(防止复用的cell展示异常数据)

所以上面方法从提高性能角度考虑,应该重新写为下面这样:

3.2. UITableView性能优化--Cell复用方式1

/**
 *  什么时候调用:每当有一个cell进入视野范围的时候调用
 */
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 0.创建cell复用标识
    static NSString *reuseIdentifier = @"cell";
    
    // 1.根据复用标识在复用池中进行查找
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
    // 2.判断如果复用池cell为nil就根据复用标识自己创建
    if (cell == nil) {
        
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    }
    
    // 3.拿到cell进行数据覆盖
    cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];
    
    return cell;
}

3.2. UITableView性能优化--Cell复用方式2

我们可以手动给tableview注册对应标识的cell,保证从缓存池中能加载到对应标识的cell。

通过代码注册

// 注册cell
static NSString *reuseIdentifier = @"cell";
[tableview registerClass:[UITableViewCell class] forCellReuseIdentifier:reuseIdentifier];

通过Xib注册

// 注册cell
static NSString *reuseIdentifier = @"cell"; // 标识可以在Xib中创建
    [tableview registerNib:[UINib nibWithNibName:@"对应的Xib名称" bundle:nil] forCellReuseIdentifier:reuseIdentifier];

在数据源方法中返回cell

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 1.根据复用标识在复用池中进行查找
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
    
    // 2.拿到cell进行数据覆盖
    cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];
    
    return cell;
}

3.3 UITableView性能优化--Cell复用方式3

第三种方式是通过UITableViewController在StoryBoard中创建,见下面:5.UITableViewController

4.UITableView的代理方法

有了数据源方法,tableview就可以正常展示数据。有了对应的代理方法就可以正常监听tableview的事件操作。

下面列举一些常用代理方法:

/**
 返回行高,可根据不同行返回不同高
 
 @param tableView tableview
 @param indexPath 位置
 @return 行高
 */
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

/**
 返回估计行高,此方法不进行真实计算,课改变Cell计算高度方法的调用顺序,
 
 @param tableView tableview
 @param indexPath 位置
 @return 行高
 */
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath;


/**
 cell的点击事件处理
 
 @param tableView tableview
 @param indexPath 位置
 */
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

/**
 cell将要被选中 -- 先需取消选中某个cell才能正常选中新的cell
 */
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
/**
 cell将要被取消选中
 */
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath 

···

5.UITableViewController

UITableViewController继承自UIViewController。可以方便创建有tableview的控制器

  • UITableViewController默认拥有tableview。在UITableViewController中 self.tableView 就是 self.view

  • UITableViewController默认继承 UITableViewDelegate和UITableViewDataSource,所有可以直接实现对应的数据源方法和代理方法即可。

  • 如果在StoryBoard中错误将UIViewController当做UITableViewController使用会报:加载不到UITableview的错误


    Snip20170321_10.png
  • 在StoryBoard中创建UITableViewController并加载cell步骤如下:

    1. 拖一个UITableViewController出来

    2. 设置initial view controller(程序第一个加载项)

    3. 修改对应的类型为自己创建的VC(如果有)

    4. 设置Dynamic Prototypes动态类型的内容,下面数量至少为1


      Snip20170321_11.png
    5. 设置cell的复用标识identfier
      Snip20170321_12.png
    6. 在代码中直接加载对应cell即可(无需判空/创建)

     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    

{
// 1.会默认去缓存池中进行查找,如果没有自动去StoryBaord中找
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];

// 2.拿到cell进行数据覆盖
cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 行",indexPath.row];

return cell;

}

```

**通过StoryBoard创建Cell更加方便**

小结

  • UITableView是iOS开发中常用的UI控件
  • UITableView需要实现数据源方法,来确定自己展示什么内容,没有数据源的UITableview只是一个空架子
  • UITableView可以通过复用UITableViewCell来实现性能优化
  • Cell复用的方式有三种(如上)
  • UITableViewController来实现带有tableview的页面更方便
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容