一:UITableView的代理,数据源代理
二:UITableView的单组数据与多组数据的展示
三:UITableViewCell的常见属性以及Cell重用机制
1:UITableView是开发中用到频率最高的视图控件。
要使用UITableView显示数据必须设置其的代理以及数据源代理,没有设置代理预计数据源代理的UITableView都是空壳。
遵守代理
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
@property(nonatomic,strong)UITableView *tabelView;
-(void)settableView
{
//UITableViewStylePlain 一般样式
//UITableViewStyleGrouped 分组样式
self.tabelView = [[UITableViewalloc]initWithFrame:self.view.boundsstyle:(UITableViewStylePlain)];
//代理
self.tabelView .delegate =self;
//数据源
self.tabelView.dataSource =self;
}
#pragma MARK __实现数据源代理与代理中必须实现的方法__
/**
UITableView中有几个分区
@param tableView 哪个tableView
@return 分区数NSInteger
*/
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return1;
}
/**
每个分区中有几行
@param tableView 哪个tableView
@param section 哪个section
@return 行数NSInteger
*/
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return10;
}
/**
设置如何显示数据
@param tableView 哪个tableView
@param indexPath 哪个indexPath
@return 返回的行视图
*/
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//重用标识
NSString * ReusabelIdentifier =@"ReusabelIdentifier";
//从缓存中获取cell
UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:ReusabelIdentifier];
//如果缓存中没有对应ID的cell
if(cell ==nil)
{//申请内存根据重用标识创建一个cell该cell会加入内存中,在需要的时候进行重用
//UITableViewCellStyleDefault,
// UITableViewCellStyleValue1,
// UITableViewCellStyleValue2,
//UITableViewCellStyleSubtitle
cell = [[UITableViewCellalloc]initWithStyle:(UITableViewCellStyleDefault)reuseIdentifier:ReusabelIdentifier];
}
//设置cell上显示什么
cell.textLabel.text =@"cellContent";
return cell;
}
/*
dataSource是UITableViewDataSource类型,主要为UITableView提供显示用的数据(UITableViewCell),指定UITableViewCell支持的编辑操作类型(insert,delete和reordering),并根据用户的操作进行相应的数据更新操作,如果数据没有更具操作进行正确的更新,可能会导致显示异常,甚至crush。
delegate是UITableViewDelegate类型,主要提供一些可选的方法,用来控制tableView的选择、指定section的头和尾的显示以及协助完成cell的删除和排序等功能。
提到UITableView,就必须的说一说NSIndexPath。UITableView声明了一个NSIndexPath的类别,主要用来标识当前cell的在tableView中的位置,该类别有section和row两个属性,前者标识当前cell处于第几个section中,后者代表在该section中的第几行。
UITableView只能有一列数据(cell),且只支持纵向滑动,当创建好的tablView第一次显示的时候,我们需要调用其reloadData方法,强制刷新一次,从而使tableView的数据更新到最新状态。
*/
UITabelView的其他常用代理方法
/**
当cell即将被显示的时候调用 在这里可以对cell内容进行进一步的修改
@param tableView 哪个UITabelView
@param cell 哪个cell
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
}
/**
cell已经完成显示的以后调用。
@param tableView 哪个UITableView
@param cell 哪个cell
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
{
}
/**
当分区的头部视图即将显示的时候调用 在这里对头部视图进行进一步的修改
@param tableView 哪个UITableView
@param view 哪个头部视图
@param section 哪个分区
*/
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
}
/**
当分区视图显示完成之后会调用
@param tableView 哪个UITableView
@param view 哪个头部视图
@param section 哪个分区
*/
- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section
{
}
/**
当分区的尾部视图即将显示的时候调用 在这里对尾部视图进行进一步修改
@param tableView 哪个UITableView
@param view 哪个尾部视图
@param section 哪个分区
*/
- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section
{
}
/**
当尾部视图完成显示的时候调用
@param tableView 哪个UITableView
@param view 哪个尾部视图
@param section 哪个分区的
*/
- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section
{
}
// Variable height support
/**
设置cell的高度
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return 高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 99.0;
}
/**
返回分区头部视图的高度
@param tableView 哪个UITableView
@param section 哪个分区的
@return 高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 120.0;
}
/**
设置分区尾的高度
@param tableView 哪个UITableView
@param section 哪个分区
@return 高度
*/
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
return 120.0;
}
/**
返回cell的预计高度
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return 高度
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 30.0;
}
/**
返回头部视图的预计高度
@param tableView 哪个UITableView
@param section 那个分区
@return 哪个indexPath
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForHeaderInSection:(NSInteger)section
{
return 44.0;
}
/**
返回尾部视图的预计高度
@param tableView 哪个UITableView
@param section 哪个分区
@return 哪个indexPath
*/
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForFooterInSection:(NSInteger)section {
return 60.0;
}
/**
返回每个分区的头部视图
@param tableView 哪个UITableView
@param section 哪个分区
@return 返回的视图
*/
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView * headView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44.0)];
[headView setBackgroundColor:[UIColor redColor]];
return headView;
}
/**
返回的尾部视图
@param tableView 哪个UITableView
@param section 哪个分区
@return 返回的视图
*/
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
UIView * footerView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 44.0)];
[footerView setBackgroundColor:[UIColor greenColor]];
return footerView;
}
/**
返回每个Cell的附件样式
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return 返回附件样式
*/
- (UITableViewCellAccessoryType)tableView:(UITableView *)tableView accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath
{
// UITableViewCellAccessoryNone
// UITableViewCellAccessoryDisclosureIndicator
// UITableViewCellAccessoryDetailDisclosureButton
// UITableViewCellAccessoryCheckmark
// UITableViewCellAccessoryDetailButton
return UITableViewCellAccessoryNone;
}
/**
当样式按钮点击的时候调用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
}
/**
cell是否能被高亮
@param tableView 哪个UItableView
@param indexPath 哪个indexPath
@return YES OR NO
*/
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
/**
cell已经高亮的时候调用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
}
/**
即将失去高亮的时候调用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
}
/**
cell即将被选择的时候调用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return 返回的indexPath
*/
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{
return indexPath;
}
/**
cell即将失去选择的时候调用 用于获取失去选择的indexPath
@param tableView 哪个UITabelView
@param indexPath 哪个indexPath
@return indexpath
*/
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
return indexPath;
}
/**
cell被选择的时候调用
@param tableView 哪个UITabelView
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//doing something when the cell be selected
}
/**
cell失去选择的时候调用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
//doing something when the cell deselect
}
// Editing
/**
返回cell的编辑类型(需要开启编辑功能)
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return 返回的编辑类型
*/
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
//无类型
// UITableViewCellEditingStyleNone,
//删除类型
//UITableViewCellEditingStyleDelete,
//插入类型
//UITableViewCellEditingStyleInsert
return UITableViewCellEditingStyleNone;
}
/**
返回编辑Confirmation按钮的title
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return 返回的title
*/
- (nullable NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
return @"Confirmation";
}
/**
返回每个Confirmation按钮点击之后需要执行的action,不需要系统默认操作的时候使用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return 返回的UITableViewRowAction类型数组。
*/
- (nullable NSArray<UITableViewRowAction *> *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewRowAction *action = [UITableViewRowAction rowActionWithStyle:(UITableViewRowActionStyleDefault) title:@"action" handler:^(UITableViewRowAction * _Nonnull action, NSIndexPath * _Nonnull indexPath) {
//
NSLog(@"action");
}];
return [NSArray arrayWithObjects:action, nil];
}
/**
是否允许cell编辑的时候缩进
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
@return YES OR NO
*/
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
/**
cell即将开始编辑的时候调用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
}
/**
cell完成编辑的时候调用
@param tableView 哪个UITableView
@param indexPath 哪个indexPath
*/
- (void)tableView:(UITableView *)tableView didEndEditingRowAtIndexPath:(nullable NSIndexPath *)indexPath
{
}
UITableViewCell的使用
给cell赋值数据对象。建议先熟悉MVC模式
MVC是一种设计思想,贯穿于整个iOS开发中,需要积累一定的项目经验,才能深刻体会其中的含义和好处
MVC中的三个角色
M:Model,模型数据
V:View,视图(界面)
C:Control,控制中心
注意:首先控制器会加载这个数据,然后根据这个数据的个数去创建对应的view
MVC的几个明显的特征和体现:
View上面显示什么东西,取决于Model
只要Model数据改了,View的显示状态会跟着更改
Control负责初始化Model,并将Model传递给View去解析展示
Cell的简介以及属性
UItableView的每一行都是一个UITableViewCell,通过datasoure的
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;方法进行编辑(该方法就是UITaleView重用机制的一个初始化cell的系统提供的方法)
UITableView内部有一个默认的字视图contentView,contentView是cell上面视图的父视图,可以显示一些辅助控件
accessoryType属性:(辅助指示视图)
辅助指示视图的作用是显示一个表示动作的图标,可以设置UITableView的accessoryType来显示。默认是不现实辅助指示图的
// UITableViewCellAccessoryNone,(不显示辅助视图)
//UITableViewCellAccessoryDisclosureIndicator(箭头)
// UITableViewCellAccessoryDetailDisclosureButton(按钮+箭头)
// UITableViewCellAccessoryCheckmark(对勾)
//UITableViewCellAccessoryDetailButton(感叹号)
[cell setAccessoryType:(UITableViewCellAccessoryDetailButton)];
Cell的常见属性
设置cell的背景颜色 backgroundColor优先级低于backgroundView
backgroundView使用方法:
创建一个View
UIView *bg = [[UIView alloc] init];
设置view的颜色,然后简介的给cell赋值
bg.backgroundColor = [UIColor redColor];
再设置cell背景的颜色
cell.backgroundView = bg;
selectedBackgroundView 使用方法:
创建一个View(也可以设置UIImageView作为cell的背景颜色)
UIView *selectedView = [[UIView alloc] init];
设置view的颜色,然后间接的赋值给cell
selectedView.backgroundColor = [UIColor redColor];
还可以通过cell的accessoryView属性来自定义辅助指示视图(比如往右边放一个开关)
注意:这里的cell的指的是通过UITableViewCell创建的一个对象的accessoryView属性来自定义辅助的视图
Cell.accessoryView = UITableViewCellAccessoryCheckmark;
cell.accessoryView = [UIbutton alloc]init];(可以在右边自定义控件可以放一个按钮或者一个开关)
注意:以上cell的属性包含了4个属性
Cell的重用原理
iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象
重用原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象
还有一个非常重要的问题:有时候需要自定义UITableViewCell(用一个子类继承UITableViewCell),而且每一行用的不一定是同一种UITableViewCell,所以一个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的UITableViewCell,那么UITableView在重用UITableViewCell时可能会得到错误类型的UITableViewCell
解决方案:UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier(一般用UITableViewCell的类名)。当UITableView要求dataSource返回UITableViewCell时,先通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就传入这个字符串标识来初始化一个UITableViewCell对象
Cell的重用代码
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
注意:重用机制是根据相同的标识符来重用cell,标识符不同的cell不能彼此重用
1.定义一个cell的标识为ID
static NSString *ID = @"mjcell";
2.从缓存池中取出cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
3.如果缓存池中没有cell
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
4.设置cell的属性...
return cell;
}
方法2:注册
另一中性能优化的写法:
0.先确定cell的重用标识
1.注册带重用标识的cell
2.从缓存池中取是否有带重用标识的cell(如果没有,系统会根据注册自动创建一个相应的cell返回给我们)
3.覆盖cell上面的数据
说明:当cell离开屏幕的时候,就会放到tableView的缓存池中,这时候缓存池才有数据
3.0 先确定cell的重用标识:命名规范:cell类型+ID
static NSString *ID = @"carID"; // static修饰局部变量,局部变量从执行后始终存在, 但不能被其它函数使用, 当再次进入该函数时, 将保存上次的结果。其它与局部变量一样。
static BOOL isReged = NO;
3.1 先注册带ID的cell
if (isReged == NO) {
warning registerCell需要注意的3点,面试可能会问到
该方法是伴随着UICollectionView出现的,也就是iOS6出现的
采用注册创建出来的cell,默认是Default样式,所以一般注册大部分都用在自定义cell的时候
需要注意的是 [tableView registerNib:<#(UINib *)#> forCellReuseIdentifier:<#(NSString *)#>] 是iOS5出现的
编码规范:xib自定义cell一般用注册
[tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:ID];
isReged = YES;
}
3.2 查看缓存池中是否有带重用标识的cell
缓存池方法中封装了,如果缓存池中没有,就根据注册创建新的cell,然后返回给我们一个带ID的cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
3.3 覆盖cell上的数据
XMGCar *car = self.cars[indexPath.row];
cell.textLabel.text = [NSString...
3.4返回cell
return cell;
}
# 方法3:storyboard
另一中性能优化的写法(storyboard):
0.先确定cell的重用标识
1.将storyboard中的tableView中的cell的重用标志赋值
2.从缓存池中取是否有带重用标识的cell
(2.1 如果没有,系统会根据注册自动创建一个相应的cell返回给我们;
2.2 如果也没有注册过,系统会根据storyboard中写好的带重用标志的cell来自动创建,然后返回)
3.覆盖cell上面的数据
说明:当cell离开屏幕的时候,就会放到tableView的缓存池中,这时候缓存池才有数据
3.返回每行内容:该方法,只有当cell显示在屏幕上的时候才会调用,是一种懒加载的思想
warning 如果有注册,就不会触发storyboard的创建新cell的机制.只有没有注册过,并且storyboard中没有标记相应cell 的时候,dequeueReusableCellWithIdentifier才会返回nil
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
3.0 先确定cell的重用标识:命名规范:数据的模型+ID
static NSString *ID = @"carID"; static修饰局部变量,局部变量只会分配一次内存地址
3.1 查看缓存池中是否有带重用标识的cell
缓存池方法中封装了,如果缓存池中没有,就根据注册创建新的cell,然后返回给我们一个带ID的cell
后面还封装了一层,如果也没有注册呢,会根据storyboard中是否标记了在重用cell,来创建一个新的cell然后返回
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
3.3 覆盖cell上的数据
XMGCar *car = self.cars[indexPath.row];
cell.textLabel.text = [N...
return cell;
}
3.修改控件属性(一半在cell的创建方法中设置initWithStyle)
cell.imageView.layer.cornerRadius = 22; 设置圆角为正方形边长一半(内切圆半径)
cell.imageView.layer.masksToBounds = YES; 设置将位于imageView之下的layer都遮盖住(默认是NO) 等效代码是:cell.imageView.clipsToBounds
设置字体以及文字颜色
cell.textLabel.textColor = [UIColor brownColor];
cell.detailTextLabel.font = [UIFont systemFontOfSize:12.0];
cell.detailTextLabel.textColor = [UIColor grayColor];
设置辅助视图
cell.accessoryType = UITableViewCellAccessoryDetailButton;