UITableView 总结


知识点回顾:
1.TableView
2.UITableViewController
3.两个协议,一个关于数据加载的协议< UITableViewDataSource >,一个关于性外和外观的协议< UITableViewDelegate >
4.对NSIndexPath类的扩展
5.tableView的单元格cell
6.响应点击事件
7.重用机制下布局子视图时需要注意的问题

一、TableView

tableView继承ScrollView。
风格:
tableView的风格有两种。
tableViewCell的预定义风格有四种。
tableViewCell右侧的按钮风格有多种。

二、UITableViewController

已经初始化好了一个UITableView的UIViewController。包括设置代理,给出了几个代理方法。没有其他特别的。

三、两个协议

知道协议的方法都有哪些功能方便选择性地实现它们。tableView的很多功能是两个协议的方法相互配合才可以实现的,因此想要实现某个功能的时候,需要同时查阅两个协议的方法。为了方便查阅,我就将两个协议的方法放在一起写,按功能分类。
两个协议定义的方法的功能可以分为4种:与外观和显示相关的功能、与行(row)的触摸事件相关的功能、与行的编辑相关的功能、与行的上下移动相关的功能。下面就按这四个功能来对协议方法进行分类整理。

1. 与外观和显示相关的功能

(1)可以分别指定cell、headerView、footerView的高度

(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
(CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section

(2)可以分别在cell、headerView、footerView显示前、显示后的对它们进行一些设置。

(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
(void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section
(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
(void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section
(void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section

(3)指定每个分区的headerView、FooterView

(nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; 
(nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section

(4)指定每一行的深度。如下图,图中,每个分区内的行的深度逐行增加

(NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath

这里写图片描述

(5)指定分区数

(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

(6)指定每个分区的行数

(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

(7)指定每一行对应的CellView,在这里绑定数据

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

(8)指定每个分区默认headerView的title、默认FooterView的title

(nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section 
(nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section

(9)指定要显示在table右侧的快速浏览列表的分区名单(section title list)

(nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView

(10)指定点击快速浏览列表的某个title时,tableView滑动到哪个section

(NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index

以上(1)-(4)为< UITableViewDelegate >协议定义的方法,(5)开始为< UITableViewDataSource >协议定义的方法。

2. 与行的触摸事件相关的功能

(1)指定每一行右侧按钮的类型,指定每一行右侧按钮的点击事件处理

(UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
(void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath

(2)指定每行被点击之后是否有颜色改变的反馈(也就是highlight强调),指定每行被强调以后的动作,指定每行失去强调以后的动作

(BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
(void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
(void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath

(3)指定某一行被选中或取消选中时真正响应的行,效果等同于重定向到别的行

(nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
(nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath

(4)指定用户选中某一行的事件处理,指定用户选中某行又离开后的事件处理

(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath

(5)指定某一行可以触发的所有方法(UITableViewRowAction)

(nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath

(6)指定是否可以弹出菜单(菜单中显示的就是上面指定的方法的title)。如果允许弹出菜单,在用户横扫某一行的时候,菜单就会出现,那么该菜单就覆盖了该行的删除按钮。也就是说,当编辑模式为删除时,用户做出与删除行相同的手势动作,就会弹出该菜单,而不会出现删除按钮。

(BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath

以上均为< UITableViewDelegate >协议定义的方法。

3. 与行的编辑相关的功能

(1)指定每一行允许的编辑模式(增、删、多选、不允许编辑)

(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath

(2)指定编辑模式为删除的情况下,每个cell的确认删除按钮的title

(nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath 

(3)指定每一行被编辑的时候,背景是否呈现凹陷的效果,默认是YES

(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath

(4)指定每一行将要开始编辑的时候、已经完成编辑的时候要执行的动作(行的编辑状态自动随table的编辑状态改变,不需要手动操作)

(void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
(void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath

(5)指定每一行里的子视图sender是否能够执行某个动作(比如某一行中含有textField,可以指定该textField中是否可以进行复制等操作,详细见UIResponder的canPerformAction:方法)

(BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender

(6)这个方法不知道干嘛的:每一行执行了某个方法以后需要进行的操作写在这个方法里???

(void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(nullable id)sender

(7)指定每一行是否可编辑

(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath

(8)用户对tableView的某一行进行了编辑操作之后,需要在这个方法里对数据进行更新,更新完数据,别忘了让tableView插入或移除新的行,或者直接reloadData。

(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath

以上(1)-(6)为< UITableViewDelegate >协议定义的方法,(7)开始为< UITableViewDataSource >协议定义的方法。

4. 与行的上下移动相关的功能

(1)指定用户完成行的移动动作以后,该行真正到达的目标位置(不一定就是用户释放该行的位置)

(NSIndexPath *)tableView:(UITableView *)tableView ```
targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath

(2)指定每一行是否可移动

(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath

(3)用户对tableView的某一行进行上下移动以后,需要在这个方法中对数据进行更新,更新完数据,别忘了让tableView重载数据(reloadData)。

(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath

以上(1)为< UITableViewDelegate >协议定义的方法,(2)开始为< UITableViewDataSource >协议定义的方法。

四、对NSIndexPath类的扩展

在NSIndexPath中扩展了两个属性和一个构造方法。

@interface NSIndexPath (UITableView)
+ (instancetype)indexPathForRow:(NSInteger)row inSection:(NSInteger)section;
@property (nonatomic, readonly) NSInteger section;
@property (nonatomic, readonly) NSInteger row;
@end

两个属性,分别是分区section和行row。
构造方法,可以用section和row来构造一个indexPath对象。

五、TableView的单元格Cell

1. 单元格的两种状态

(1)编辑状态下的状态:
这里写图片描述
(2)非编辑状态下的状态
这里写图片描述

2. 自定义单元格

(1)方式:通过继承UITableViewCell来实现。
(2)初始化:由于表格的重用机制会自动调用单元格的初始化方法,也就是initWithStyle:reuseIdentifier:方法,因此要为自定义单元格重写这个方法,在这个方法中对(某些固定的)子视图进行初始化。
(3)子视图的布局:为了适应不同尺寸,以及屏幕的横屏竖屏状态,应该在自定义单元格的layoutSubviews方法里面对子控件的frame进行设置。别忘了,tableView自身的frame也需要设置(不过是在Controller的layoutSubviews方法里)。单元格的layoutSubviews方法调用得很频繁,每次单元格进入表格的可视范围内都会调用一次。
(4)数据的绑定:对于需要绑定数据、显示数据的单元格,比较好的做法是将数据封装成一个类。给单元格添加一个这个数据类的属性,然后重写该属性的setter方法,在setter方法中,一次性实现属性的赋值,以及数据与视图的绑定。对于子视图的个数与数据内容相关的情况,子视图的创建也都是在这个setter方法中完成。
另外,为了减少代码被不必要地重复执行,在setter方法中可以加入一条判断语句,判断一下新的对象和旧的对象是否相等,如果不相等才需要进行赋值,以及后面一系列对子视图的操作。
(5)数据类:上面所述的数据类,就是MVC设计模式中的M,Modal。数据类通常只包含呈现表格内容所需的信息。但是,数据的来源(其他的文件、网络等)也许包含了很多其他的信息。这样,当我们已经成功将数据提取到一个字典中,这个字典也会有很多无用的key-value pair,这些key在数据类中是没有的。此时,利用KVC特性,直接拿字典来初始化数据类,程序会因为字典中含有很多对于数据类来说是未定义的key而崩溃。解决这个问题只需要重写一下setValue:forUndefinedKey:方法就可以了。重写这个方法,不仅可以解决上述问题,还可以解决字典中某个key和数据类中对应的属性名不一致的问题。

六、响应点击事件

1. 定义

点击事件,对于表格来说,准确表述应该是表格的某一行被选中的事件(如果不考虑在cell中自己安插的按钮)。

2. 行是否可以被选中

非编辑状态下是否可以被选中,通过tableView的allowsSelection属性来设定
在编辑状态下是否可以被选中,通过allowsSelectionDuringEditing属性来设定

3. 响应方法:

(1)行被选中的响应方法
tableView:didSelectRowAtIndexPath:
这是个协议方法,这个方法的实现中,注意先把该行由选中状态变为未被选中状态以后,再去实现其他的响应动作。让某一行回到未选中状态的方法是这个deselectRowAtIndexPath:animated:
(2)其他点击事件的响应方法:
如果用户点击的是系统定义的accessory按钮,那么响应方法是tableView:accessoryButtonTappedForRowWithIndexPath:
如果用户点击的是用户自定义的accessory按钮,那么响应方法就是这个按钮的target和action指定的方法。

4. 用代码替代用户的触摸动作

(1)让tableView滑动到某个位置
scrollToRowAtIndexPath:atScrollPosition:animated:
indexPath指定了滑动之后要看得到某一行
scrollPosition用来指定了让scrollView滑动哪里位置范围,参数类型是个枚举值:

typedef enum {
   UITableViewScrollPositionNone,
   UITableViewScrollPositionTop,
   UITableViewScrollPositionMiddle,
   UITableViewScrollPositionBottom 
} UITableViewScrollPosition;

(2)让tableView滑动到靠近被选中的行的位置
scrollToNearestSelectedRowAtScrollPosition:animated:
(3)让某一行被选中,并且让scrollView滑动到该行的位置
selectRowAtIndexPath:animated:scrollPosition

七、重用机制

1. 对重用机制的理解

tableView可以使用不同类型的cell来呈现数据。重用机制就是:把需要用到的类型都登记一下,如果当前数据的呈现需要用到某个类型的cell,系统就会调用该cell类的初始化方法initWithStyle:reuseIdentifier:来实例化一个对象,并与这个数据进行绑定,然后呈现在tableView中。当这个cell移出了tableView的可视范围,就会被加入重用池,如果有一个新数据也需要用这种类型的cell来呈现的话,就会从重用池中取出这个cell对象,绑定新的数据,然后呈现在tableView中。这样就实现了cell对象的重用。

2. 重用机制下管理cell的子视图布局时需要注意的问题

需要注意的是,cell对象每次在呈现到tableView上之前都会调用一下其对应类的layoutSubviews方法。在这个方法执行之前,系统已经按照新数据的要求更新了cell的大小(也就是self.frame.size或者self.bounds.size),但是没有更新这个cell的contentView的大小。也就是说,如果这个cell对象是刚刚初始化好,第一次拿来用,那么cell的contentView的大小和cell的大小是一样的。但是如果这个cell是从重用池中拿出来的,那么cell的contentView的大小还是上一次使用的时候的大小。因此,在对子控件的位置进行布局的话,尽量相对于cell的bounds。如果非要相对于contentView的大小来布局子视图,就要记得先执行self.contentView.bounds=self.bounds;来更新一下contentView的大小。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值