UITableView继承于UIScrollView,可以滚动。 UITableView的每一条数据对应的单元格叫做Cell,是UITableViewCell一个对象,继承于UIView。 UITableView可以分区显⽰示, 每一个分区称为section, 每一⾏称为row, 编号都从0开始。 系统提供了一个专门的类来整合section和row,叫做NSIndexPath。
//创建UITableView
UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.boundsstyle:UITableViewStylePlain];
[self.view addSubview:tableView];
[tableView release];
//UITableView的样式枚举
// UITableView的初始化⽅方法包含⼀一个UITableViewStyle类型的参数 这是⼀一个枚举类型
typedef NS_ENUM(NSInteger, UITableViewStyle)
{ UITableViewStylePlain, UITableViewStyleGrouped };
//UITableView的相关属性
//rowHeight 行高
//separtorStyle 分隔线样式
//separtorColor 分隔线颜⾊
//tableHeaderView UITableView的置顶视图
//tableFooterView UITableView的置底视图
//UITableView中有两个重要的属性:
@property (nonatomic, weak, nullable) id dataSource;
//dataSource 显示数据相关的代理 @property (nonatomic, weak, nullable) id delegate;
//delegate 视图操作相关的代理
//UITableView代理的实现代码
1.签订UITableView协议
2.设置当前的ViewController为 UITableView
//UITableViewCell
//UITableView的每一个单元格是UITableViewCell类的对 UITableViewCell默认提供了3个视图属性:
UIImageView *imageView 图片视图
UILabel *textLabel 标题视图
UILabel *detailTextLabel 副标题视图
//UITableView重用cell的代码流程
1. 在创建UITableView之后,需要注册一个cell类,当重用池中没有 cell的时候,系统可以自动创建cell。 相关方法: - (void)registerClass:(Class)cellClass forCellReuseIdentifier: (NSString *)identifier; 2.系统提供了一个获取重⽤用池中cell的⽅方法(需要提供一个重用标 识): - (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;
//UITableViewController
UITableViewController是继承于UITableViewController中的一个 类,只不过⽐比UITableViewController中多了一个属性tableView。 即:UITableViewController是一个自带table的视图控制器。
一:UITableViewController继承自UITableViewController,自带 一个ableVie
二:[self.view]不是UIView而是UITableView
三:datasource和delegate默认都是 self(UITableViewController)
四:开发中只需要建立UITableViewController子类
UITableView 需要签订两个协议,一个是datasource,一个是delegate
dataSource:
UITableViewDataSource类型
主要为UITableView提供显示用的数据(UITableViewCell),指定UITableViewCell支持的编辑操作类型(insert,delete和reordering),并根据用户的操作进行相应的数据更新操作,如果数据没有更具操作进行正确的更新,可能会导致显示异常,甚至crush。
//调用数据源的下面方法得知一共有多少组数据
- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView;
//调用数据源的下面方法得知每一组有多少行数据
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section;
//调用数据源的下面方法得知每一行显示什么内容
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath;
delegate:
UITableViewDelegate类型,主要提供一些可选的方法,用来控制tableView的选择、指定section的头和尾的显示以及协助完成cell的删除和排序等功能。
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section;
这里注意:如果不是cell有不同高度的需求,在设置cell的高度上,最好不要用这个,直接在创建tableview的时候,设置rowHeight即可。
accessoryType属性:
辅助指示视图的作用是显示一个表示动作的图标,可以通过设置UITableViewCell的accessoryType(其属性)来显示,默认是UITableViewCellAccessoryNone(不显示辅助指示视图),其他值如下:
UITableViewCellAccessoryDisclosureIndicator 箭头
UITableViewCellAccessoryDetailDisclosureButton 按钮+箭头
UITableViewCellAccessoryCheckmark 对勾
UITableViewCellAccessoryNone 没有
UITableViewCellAccessoryDetaiButton 叹号按钮
UITableViewCellStyle四种样式
cell的重用原理
iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象
原理:
当滑动列表时,部分cell会移处窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource会用新的数据配置这个UITableViewCell,然后返回给UITableView,重新显示到窗口中,从而避免创建新对象
注意:
有时候需要自定义UITableViewCell,而且每一行用的不一定是同一种cell,所以一个UITableView可能拥有不同类型的UITableViewCell。对象池中也会有很多不同类型的UITableViewCell,那么在UITableView重用cell时可能会得到错误类型的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
staticNSString*ID =@"mjcell";
2.从缓存池中取出cell
UITableViewCell*cell = [tableView dequeueReusableCellWithIdentifier:ID];
3.如果缓存池中没有cell
if(cell ==nil) {
cell = [[UITableViewCellalloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
4.设置cell的属性...
return cell;
}
方法2:注册
[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]
拓展一:UITableView的cell侧滑删除按钮
//1.该行能不能编辑
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPaht *)indexPath{
return YES;
}
//2.提交编辑
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
//以下语句不能调换顺序,不然删除会崩溃报错
//1.删除对应数据源中的方法
[self.data removeObjectAtIndex:indexPath.row];
//2.删除界面上的哪一行
[tableView deleteRowsAtIndexPath:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
//3.设置删除按钮的文字(默认是英文的delete)
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath{
return @"删除";
}
拓展二:UITableView 的cell增加
先创建一个按钮,在按钮点击事件里设置进入编辑状态
[self.tableView setEditing:YES];
点击增加按钮,cell进入编辑状态,默认还是删除。想变成增加,需下面这个方法
//增加cell
-(UITableViewCellEditingStyle)tableview:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
//默认情况下是删除
return UITableViewCellEditingStyleInsert;
}
这个时候,-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath这个方法就需要做出判断,如果是删除style执行删除cell,如果是增加style那就执行增加
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
if(editingStyle == UITableViewCellEditingStyleDelete){
//以下语句不能调换顺序,不然删除会崩溃报错
//1.删除对应数据源中的方法
[self.data removeObjectAtIndex:indexPath.row];
//2.删除界面上的哪一行
[tableView deleteRowsAtIndexPath:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}else if(editingStyle == UITableViewCellEditingStyleInsert){
//增加数据源
[self.data insertObject:@"新数据" atIndex:indexPath.row];
//刷新整个表格
//[self.tableView reloadData];(这里需要注意,如果想刷新tableview,用这个方法是没问题,但是这个是刷新整个表格,如果只是单纯增加或删除一行cell,没必要刷新整个表格,来耗内存)
[tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
拓展三: UITableView 的cell移动
先创建一个按钮,在按钮点击事件里设置进入编辑状态
[self.tableView setEditing:YES];
进入编辑状态后,tableviewcell的右边会出现一个图标,进行拖拽到你想移动的cell位置
//移动cell
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath{
return YES;
}
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
//1.移除需要移动的cell
NSString *cellData = self.Data[sourceIndexPath.row];
//2.移除原始位置的数据
[self.data removeObjectAtIndex:sourceIndexPath.row];
//3.往目标位置插入一个数据
[self.data insertObject:cellData atIndex:destinationIndexPath.row];
(注意:如果没有以上这三点,只有两个移动cell函数的话,移动后的cell,在你滑动tableview的时候,数据还会变成原本数据,需要你对移动的数据进行重新处理)
}
最后,编辑完成,根据项目需求,在你点击完成的事件函数里,设置[self.tableView setEditing:NO];
为什么很多内置类如UITableViewController的delegate属性都是assign而不是retain呢?
会引起循环引用----若是retain,在alloc一次之后,若release一次,会导致内训泄漏,若release两次会导致两个 对象的dealloc嵌套执行,结果就是都没有执行成功,最后崩溃! 所有的引用计数系统,都存在循环应用的问题。例如下面的引用关系:
* 对象a创建并引用到了对象b. * 对象b创建并引用到了对象c. * 对象c创建并引用到了对象b.
这时候b和c的引用计数分别是2和1。 当a不再使用b,调用release释放对b的所有权,因为c还引用了b,所以b的引用计数为1,b不会被释放。 b不释放,c的引用计数就是1,c也不会被释放。从此,b和c永远留在内存中。 这种情况,必须打断循环引用,通过其他规则来维护引用关系。我们常见的delegate往往是assign方式的属性而不是 retain方式 的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。 如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的 delegate又是a,如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用 delegate模式时,也要注意这点。