UITableView(一)----基本介绍
一、简介
1. 继承关系
UITableView继承自UIScrollView,因此支持垂直滚动,而且性能极佳。UITableView的代理协议也继承了UIScrollView的代理协议,可以通过实现UIScrollView的代理方法,监听UITableView的变化。在UITableView中没有列的概念,只有行的概念,数据都是按行显示的。
2.使用场景
在iOS中,通常使用UITableView实现表格数据展示
3. 基本概念
Plain:单组样式,整个UITableView只有一组数据
Grouped:多组样式,整个UITableView有多组数据
dataSource:数据源,用于设置UITableView展示的数据
delegate:代理,用于监听UITableView的操作和对UITableView的一些属性进行设置
row:行,在每一行上展示数据,每一行对应一个cell
section:组,UITableView中可以把相似的内容分维一组
Header:头部,一组的头部
Footer:尾部,一组的尾部
estimatedHeight:估计高度,有估计行高、估计头部高度、有估计尾部高度;给系统一个估算高度,系统会先创建一个cell,然后再调用设置行高的方法
Selection:选中
reload:刷新,创新装载数据
缓冲池获取cell:在缓存池中获取带有对应标记的cell
4. Cell的简介
(1).UITableView的每一行都是一个UITableViewCell对象,通过dataSource的tableView:cellForRowAtIndexPath:方法来初始化每一行
(2).常见属性
imageView:用于显示图片
textLabel:用于显示大标题
detailTextLabel:用于显示小标题
contentView:contentView是UITableViewCell所显示内容的父视图,可显示一些辅助指示视图,自定义cell的子控件都添加到contentView中
backgroundView:添加一个背景视图,可以用于设置cell普通状态下的背景颜色
selectedBackgroundView:选中一个cell之后的背景视图,可用于设置选中时候的背景颜色
multipleSelectionBackgroundView:选中多个时候的背景视图,可用于设置选中多个时候的背景颜色
reuseIdentifier:用于给cell设置一个标识,设置标识的cell用于性能优化
selectionStyle:选中时候的显示类型
selected:设置是否选中
highlighted:是否在选中的时候呈现高亮
(3).常见方法
// 初始化一个cell和设置cell的标识 - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier NS_AVAILABLE_IOS(3_0);
二、常见属性
// UITableView类型,类型有单组和多组 @property (nonatomic, readonly) UITableViewStyle style; // 数据源 @property (nonatomic, assign) id dataSource; // 代理 @property (nonatomic, assign) id delegate; // 行高 @property (nonatomic) CGFloat rowHeight; // 组的头部高度 @property (nonatomic) CGFloat sectionHeaderHeight; // 组的尾部高度 @property (nonatomic) CGFloat sectionFooterHeight; // 行的估计高度 @property (nonatomic) CGFloat estimatedRowHeight NS_AVAILABLE_IOS(7_0); // 组的头部的估计高度 @property (nonatomic) CGFloat estimatedSectionHeaderHeight NS_AVAILABLE_IOS(7_0); // 组的尾部的估计高度 @property (nonatomic) CGFloat estimatedSectionFooterHeight // 设置背景视图 @property(nonatomic, readwrite, retain) UIView *backgroundView NS_AVAILABLE_IOS(3_2); // 设置头部显示的视图 @property (nonatomic, retain) UIView *tableHeaderView; // 设置尾部显示的视图 @property (nonatomic, retain) UIView *tableFooterView; // 是否可以编辑 @property (nonatomic, getter=isEditing) BOOL editing;
三、常见方法
1. 关于TableView初始化
// 初始化一个tableView和设置tableView的样式 - (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style;
2. 关于TableView数据更新
// 所有数据全部更新 - (void)reloadData; // 更新索引 - (void)reloadSectionIndexTitles NS_AVAILABLE_IOS(3_0);
3.关于TableView的信息
// 获取UITableView有多少组 - (NSInteger)numberOfSections; // 获取第section组有多少行 - (NSInteger)numberOfRowsInSection:(NSInteger)section; // 获取第section的位置和尺寸大小 - (CGRect)rectForSection:(NSInteger)section; // 获取第section的头部的位置和尺寸大小 - (CGRect)rectForHeaderInSection:(NSInteger)section; // 获取第section的尾部的位置和尺寸大小 - (CGRect)rectForFooterInSection:(NSInteger)section; // 获取第indexPath.section组第indexPath.row行的位置和尺寸大小 - (CGRect)rectForRowAtIndexPath:(NSIndexPath *)indexPath; // 获取原点point所在的行的位置indexPath - (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point; // 获取指定的cell的位置indexPath - (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell; // 获取rect范围内的所有行的位置indexPath所组成的数组 - (NSArray *)indexPathsForRowsInRect:(CGRect)rect; // 获取indexPath位置的cell - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath; // 获取看得见的所有的cell所组成的数组 - (NSArray *)visibleCells; // 获取看得见的所有的cell的位置indexPath所组成的数组 - (NSArray *)indexPathsForVisibleRows; // 获取第section组头部的view - (UITableViewHeaderFooterView *)headerViewForSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 获取第section组尾部的view - (UITableViewHeaderFooterView *)footerViewForSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);
4. 关于行或组的插入、删除、更新
// 配合beginUpdates方法使用,在一个代码块中,提示开始更新,代码块中写对行或组的插入、删除 - (void)beginUpdates; // 配合beginUpdates方法使用,在一个代码块中,提示结束更新,代码块中写对行或组的插入、删除 - (void)endUpdates; // 插入组数据 - (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; // 删除组数据 - (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation; // 更新组数据 - (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0); // 移动组 - (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection NS_AVAILABLE_IOS(5_0); // 插入行 - (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 删除行 - (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation; // 更新行 - (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0); // 移动行 - (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath NS_AVAILABLE_IOS(5_0);
5.关于选中的方法
// 获取选中行的位置indexPath - (NSIndexPath *)indexPathForSelectedRow; // 获取选中多行的位置indexPath所组成的数组 - (NSArray *)indexPathsForSelectedRows NS_AVAILABLE_IOS(5_0);
6. 关于cell的重利用方法
// 重利用标识为identifier的cell,如果注册过Cell,在没有可用的cell时,会返回nil - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier; // 重利用标识为identifier的cell,如果你没有注册过cell,在没有可用的cell时,程序直接崩溃 - (id)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); // 重利用标识为identifier的自定义的头部或尾部 - (id)dequeueReusableHeaderFooterViewWithIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
7.关于cell的注册方法
// 注册xib创建的cell,设标识为identifier - (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0); // 注册cellClass创建的cell,设标识为identifier - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0); // 注册xib创建的头部或尾部,设标识为identifier - (void)registerNib:(UINib *)nib forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0); // 注册aClass创建的头部或尾部,设标识为identifier - (void)registerClass:(Class)aClass forHeaderFooterViewReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);
四、数据源
要让tableView显示数据,要设置数据源,并实现必要的数据源方法
// 设置第section组的行数,必选 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; // 设置位置为indexPath的cell,必选 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; // 设置tableView的组数,不实现该方法就默认只有一组 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // 设置第section组的头部 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection(NSInteger)section; // 设置第section组的尾部 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section; // 设置位置为indexPath的是否可以编辑,与tableview:commitEditingStyle:方法配合使用 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath; // 当某一行可进行编辑时,实现该方法可以对那一行进行编辑 - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath; // 设置位置为indexPath的是否可以移动, 与tableview:moveRowAtIndexPath方法配合使用 - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath; // 移动时使用 - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath; // 给组添加索引 - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView; // 设置每一组对应的索引,index表示组号,title表示索引标题 - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index;
五、代理
tableView的代理可以实现scrollView的代理方法,因为协议继承了scrollView代理协议
1.关于监听显示的方法
// 即将显示位置为indexPath的cell时调用 - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; // 即将显示第section组的头部时调用 - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 即将显示第section组的尾部时调用 - (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 位置为indexPath的cell不再在屏幕上显示时调用 - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath NS_AVAILABLE_IOS(6_0); // 第section组的头部不再在屏幕上显示时调用 - (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0); // 第section组的尾部不再在屏幕上显示时调用 - (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section NS_AVAILABLE_IOS(6_0);
2.关于设置高的方法
// 设置行高 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath; // 设置头部的高 - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section; // 设置尾部的高 - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;
3.关于设置估计高的方法
// 估计行高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(7_0); // 估计头部的高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0); // 估计尾部的高 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section NS_AVAILABLE_IOS(7_0);
4.关于自定义头部或尾部标题的view的方法
// 设置自定义View的头部 - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; // 设置自定义view的尾部 - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;
六、性能优化
1.问题
iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象
2.苹果已经进行一次优化
优化:看到的cell才创建,看不见的cell就销毁。
但其实苹果没有销毁cell,只是把它放到了缓存池中
3.程序员的再次优化
思想:当要显示新的cell时,如果缓存池中有类似的cell就直接拿过来重用
(1).重用原理
当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象。有时候需要自定义UITableViewCell(用一个子类继承UITableViewCell),而且每一行用的不一定是同一种UITableViewCell,所以一个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的UITableViewCell,那么UITableView在重用UITableViewCell时可能会得到错误类型的UITableViewCell
(2).解决方案
UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier(一般用UITableViewCell的类名)。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就用传入的这个字符串标识来初始化一个新的UITableViewCell对象
七、注意
1.同时设置cell的选中背景View和默认背景View,设置默认背景View优先级更高
2.在对cell进行编辑操作时,模型和cell的行数必须一致,不然会报错
3.在storyboard中的tableView中添加cell,可以拖控件UITableViewCell,也可以在tableView的属性栏设置Prototype Cells的个数
4.不设置tableView的数据源,即使实现了数据源协议的方法也不能显示数据
5.不设置tableView的代理,即使实现了代理协议的方法也不能显示数据
6.关于估计高度:只要返回了估计高度,那么就会先调用tableView:cellForRowAtIndexPath:方法创建cell,再调用tableView:heightForRowAtIndexPath:方法获取cell的真实高度
7.当控件的约束刚设置时,如果要紧接着使用控件的Frame,要先调用layoutIfNeeded方法进行强制布局,系统才对控件的Frame进行设置,否则Frame为0
8.设计自定义cell时,应该先把所有可能出现的控件布置好,哪些暂时不需要显示的可以隐藏,在需要显示时再取消隐藏
9.应该充分利用模型,因为模型始终贯穿tableView,可以用于记录一些cell的不确定的属性,如不等高时,用模型记录每一个cell不同的高