欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

UITableView(一)----基本介绍

程序员文章站 2022-04-07 10:09:29
一、简介 1. 继承关系 UITableView继承自UIScrollView,因此支持垂直滚动,而且性能极佳。UITableView的代理协议也继承了UIScrollView...

一、简介

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不同的高