Objective-C UI之UITableView详解

UITableView在iOS开发中占据非常重要的位置,必须熟练掌握。
学习UITableView之前,先了解一下一些基本概念:

  • UITableView继承于UIScrollView,是可以进行垂直滚动的控件
  • UITableView的每一条数据对应的单元格叫做Cell,是UITableViewCell的一个对象,继承于UIView
  • UITableView可以分区显示,每一个分区称为section,每一行称为row,编号都从0开始
  • 系统提供了一个类来整合section和row,叫做NSIndexPath

从上面可以了解到,section和row代表一个UITableViewCell在UITableView上的位置
下面,我们创建一个UITableView:

//style是一个UITableViewStyle类型的参数,是一个枚举类型,包含UITableViewStylePlain,UITableViewStyleGrouped
UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];

[self.view addSubview:tableView];

下面是UITableView的常用属性:

rowHeight 行高
separatorStyle 分隔线样式
separatorColor 分隔线颜色
tableHeaderView UITableView的置顶视图
tableFooterView UITableView置底视图

一、UITableView基础

UITableView中有两个重要的属性:dataSource(遵循UITableViewDataSource协议)和delegate(遵循UITableViewDelegate协议)
其中dataSource是和显示数据相关的代理,delegate是和视图操作相关的代理
UITableViewDataSource协议中有两个必须实现的协议方法:
1.UITableView每个分区包含的行数:

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
    2.每一行要显示的Cell:
  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
    第一个方法可以根据给出的参数section不同返回不同的行数
    第二个方法:tableView每次要显示一个Cell都会调用这个方法获取

UITableView的每一个单元格是UITableViewCell类的对象,默认提供了三个视图属性:

  • 图片视图:UIImageView *imageView
  • 标题视图:UILabel *textLabel
  • 副标题视图:UILabel *detailTextLabel
    下面是返回Cell的例子:(没有使用registerClass注册的情况)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"cell";
    //通过标识符,在tableView的重用池中找到可用的Cell(在重用池内部其实是一个可变的集合)
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    //如果重用池中没有这个标识符对应的cell,则创建一个新的,并且设置标识符
    if (!cell) {
        //代码块内只做Cell样式的处理,不做数据设置
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:cellID];
    }
    //对Cell做数据设置
    [cell.textLabel setText:@"标题"];
    [cell.detailTextLabel setText:@"描述:这是小标题"];
    return cell;
}

UITableView有一个重用池机制管理Cell,目的是使用尽可能少的Cell显示所有的数据
UITableView重用Cell的流程

  1. 当一个Cell被滑出屏幕,这个Cell会被系统放到相应的重用池中
  2. 当tableView需要显示一个Cell,会先去重用池中尝试获取一个Cell
  3. 如果重用池没有Cell,就会创建一个Cell
  4. 取得Cell之后会重新赋值进行使用

在创建UITableView之后,需要注册一个Cell类,当重用池中没有Cell的时候,系统可以自动创建Cell。相关方法:
[tableView registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier];(可以使用不同identifier进行多次注册)
系统提供了一个获取重用池中Cell的方法(需要一个重用标识):

- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;

UITableView的常用协议方法

1.UITableViewDataSource

  • UITableView分区个数:
    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  • 分区的顶部标题:
    - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  • 分区的底部标题:
    - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
  • UITableView右侧的索引录:
    - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView

2.UITableViewDelegate

  • 告诉delegate选中了一个Cell:
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  • 每一行的高度:
    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  • 每一个分区的顶部高度:
    - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
  • 每一个分区的顶部自定义视图:
    - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section

二、UITableView编辑

流程:

  • 让tableView处于编辑状态:[tableView setEditing:(BOOL)editing animated:(BOOL)animated];
  • 确定Cell是否处于编辑状态:
//重写UITableViewDataSource的协议方法,根据indexPath决定哪些Cell处于编辑状态,返回YES是可编辑,NO为不可编辑
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;
  • 设定Cell的编辑样式(删除、添加):
//重写UITableViewDelegate的协议方法,根据indexPath可以决定Cell的编辑样式,是添加UITableViewCellEditingStyleInsert还是删除UITableViewCellEditingStyleDelete,还是不进行编辑UITableViewCellEditingStyleNone
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
  • 编辑状态进行提交:
//重写UITableViewDataSource协议方法,根据editingStyle删除indexPath位置的Cell,还是在indexPath处插入Cell,修改数据源
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath;

注意:编辑结束后,由于numberOfRowInSection这个协议方法只在tableView添加到父视图的时候调用一次,而且table上的数据都是由数组提供,因此,需要先改变数组中的数据,然后让table的协议重新调用进行重新赋值
即先修改数据源,在刷新table(使用[table reloadData]方法刷新)

三、UITableView移动

  • 实现协议,告诉tableView是否能够移动:
//返回YES允许移动
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;
  • 移动:
//修改数据源,再进行[tableView reloadData]更新tableView视图数据
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath

四、UITableViewCell

1.自定义Cell

  1. 创建一个类继承于UITableViewCell
  2. 实现UITableViewCell的初始化方法:- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
  3. 确保所有的添加的子视图都在自定义Cell的初始化方法中创建,避免子视图的重复创建
  4. 在Cell的子视图创建成功后,将子视图设置为属性,便于在UITableView的协议中给自定义视图赋值

一般而言,Cell在创建的时候的frame大小是(0,0,320,44),而我们设定的Cell的高度一般会大于44。因此:在自定义Cell中创建子视图的frame为CGRectZero。在Cell添加到tableView上的时候才给子视图设置frame,Cell添加到tableView的时候大小已经更改为tableView设定的大小,所以在自定义Cell的方法layoutSubviews中设置子视图的frame

2.Model的使用
Model类的作用主要是为我们提供数据,一般我们的数据都是存放在数组和字典中,OC中的KVC就是帮助我们将字典转换为Model类而存在的
使用步骤:

  1. 创建一个Model类继承于NSObject
  2. 添加和字典中对应的属性,属性名要和字典key值相同,除系统关键字外
  3. 在视图控制器中将字典通过KVC为Model赋值
  4. 将Model对象添加到数组中并刷新tableView

注意:Model类要重写-(void)setValue:(id)value forUndefinedKey:(NSString *)key,防止找不到和key值相同的属性时,会crash,当key值为系统关键字,可以在方法里面为对应的属性(属性名和系统关键字不冲突)赋值,比如_id = value;

3.多种Cell混合使用
不同的Cell需要使用不同的重用标识符来进行区分,而重用标识符的区分需要根据不同的情况来区分,比如:

  • Model属性区分(不同的数据内容,比如数据中有type字段,0代表文字类型,1代表图片类型)
  • 固定的行显示的Cell类型不一样

4.自适应高度

  • 文本自适应高度:
//获得字体样式属性
NSDictionary *att = @{NSFontAttributeName:[UIFont systemFontOfSize:18.0]};
//根据字体样式属性获得高度
CGRect rect = [string boundingRectWithSize:CGSizeMake(300,MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:att context:nil];
//重新设置显示文本控件的frame
[self.label setFrame:CGRectMake(0,0,300,rect.size.height)];

//我们可以定义一个类专门用来计算文本高度,这样可以使用时直接调用
  • Cell自适应高度:
通过协议方法- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath设定Cell的高度

在自定义Cell中的layoutSubviews方法中设定子视图的高度

转载请注明:作者SmithJackyson

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

推荐阅读更多精彩内容