
带你玩装UITableView
在实际iOS开发中UITableView是使用最多,也是最重要的一个控件,如果你不会用它,那别说什么大神了,菜鸟都不如。
其实关于UItableView事非常简单的,实际开发中用起来却没有那么简单就是因为他结合MVC使用,涉及到了模型数据的读取,自定义View,功能的拓展和更好的解藕,下面就带你玩一遍;
UITableView的两种样式
- UITableViewStylePlain
- UITableViewStyleGroupeds
accessoryType
- UITableViewCellAccessoryDisclosureIndicator
- UITableViewCellAccessoryDetailButton
- UITableViewCellAccessoryDetailDisclosureButton
- UITableViewCellAccessoryCheckmark
@required 代理必须实现的方法,如果不实现这些方法,控件无法正常工作
* 告诉表格要显示多少行的内容
- - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
* 要显示的表格行视图
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
@optional 代理不一定需要实现的方法,如果使用某些高级功能的时候,才需要实现这些方法
* 表格数据中有多少分组
- - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
* 每个分组的标题文字
- - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
* 每个分组的尾部文字
- - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;
提示:
1> 代理方法的返回值,是代理告诉控件(委托)的相关细节
2> 在定义协议方法的时候可以使用
- @required 指定代理必须要实现的方法,否则控件无法正常工作
- @optional 指定代理可选的实现方法,当需要监听某些特殊事件,或获取某些特殊数据时使用
不是所有的代理都需要实现可选方法,可以让自定义控件的灵活度更大!
跟踪数据源方法的执行先后顺序,提示学习和思考的方式,如果
1> 代理方法的命名规范
(1) 协议的名称前半部分与控件名称相同
* 末尾添加"DataSource"表示代理可以通过这些协议方法向控件传递数据
* 末尾添加"Delegate"表示代理实现这些方法,可以在控件发生某些事件时做出响应的动作
(2) 协议方法
* 通常以控件名称(去除类前缀)开头,方便程序编写
* 第一个参数是控件自身,方便在代理方法中直接访问控件属性或者调用方法,而无需再传递更多的额外参数
问题: 表格中的数据是静态的,不够灵活
解决方法: 引入数据模型,将数据与界面分离
1> 使用代码的方式对此前的代码进行改造,进一步体会模型的好处以及MVC架构的设计
2> .语法与set方法的使用对比
3> 搭建界面,在Storyboard中之间做数据源的连线
* 运行会崩掉!
* 原因分析,没有实现数据源方法,进一步体会@required定义的意义
4> 编写数据源方法,只要实现两个必须的方法就可以了
5> UITableViewCell的深入探索
* detailTextLabel 明细内容
6> 要显示明细内容需要修改tableViewCell的样式
- UITableViewCellStyleDefault, // 标签 + 图像
- UITableViewCellStyleValue1, // 标签 + 明细 + 图像
- UITableViewCellStyleValue2, // 标签 + 明细
- UITableViewCellStyleSubtitle // 标签 + 明细 + 图像
7> 设置行高的两种方式
* 使用tableView的rowHeight属性可以统一设置表格行高,效率更高
* 通过代理方法设置行高,可以在
(1) 通过表格数据源方法,知道表格行数之后计算所有单元格的行高,效率不高
(2) 计算获得所有单元格高度后,再创建屏幕显示的单元格
体会懒加载的好处,只有在需要的时候才会创建!
提示:计算行高的时候,单元格还没有被创建
应用场景:类似于新浪微博之类的应用程序,每个单元格的行高都是需要根据内容进行调整,是不固定的
6. TableViewCell的循环引用
1> 使用Reveal查看UITableView的实际布局
2>
- - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
方法会在每次一个TalbeViewCell出现在屏幕中时被调用
为了避免为单元格重复分配内存空间,UITableView提供了循环引用机制
- 1> dequeueReusableCellWithIdentifier可以使用指定的标示符在缓冲池中查找可重用Cell
- 2> 如果没有找到cell再实例化cell
- 3> 使用static修饰符可以保证字符串变量只被分配一次内存空间,在此可以与宏的方式进行一下对比
重用机制:
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对象
代码实现:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 1.定义一个cell的标识 static NSString *ID = @”czcell"; // 2.从缓存池中取出cell UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 3.如果缓存池中没有cell if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID]; } // 4.设置cell的属性... return cell; }
重点:加载Plist数据转成模型
- (NSArray *)wineArray { if (!_wineArray) { // 加载字典数组 NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"wine.plist" ofType:nil]]; // 将字典数组 -> 模型数据 NSMutableArray *wineArray = [NSMutableArray array]; for (NSDictionary *dict in dictArray) { [wineArray addObject:[XMGWine wineWithDict:dict]]; } _wineArray = wineArray; } return _wineArray; }
//直接使用MJExtension
- (NSArray *)tgs { if (!_tgs) { _tgs = [XMGTg objectArrayWithFilename:@"tgs.plist"]; } return _tgs; }
关于MJExtension请看前面的文章:plsit转模型详细介绍,或者你可以直接去github上面搜索看他的官方教程
// accessoryButton点击监听方法
- - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath;
// 选中行操作
- - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
添加索引
/** 返回右边显示的索引数组 */
"注意" 索引数组的下标与数据分组索引的下标对应,而与索引的文字无关
- - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView;
辅助方法
- - (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index
/**
只要实现了此方法,TableView就可以对TableView进行编辑
*/
- - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
// 代理方法 - 默认返回删除样式
- - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
// 只要实现了此方法,就可以进行数据排序,由于UI上已经完成了表格行的交换,此方法中只要实现数据交换即可
- - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath;
注意:[tableView reloadData];的执行效率,本方法通常在对数据改变未知的情况下使用。
自己来编写类似的功能,可以参照这些实现思路
/****************************************简单UITableView实现*******************************************************/
首先我们来看看最简单的方法实现UITableView
#pragma mark - <UITableViewDataSource> - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { ; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { ) ; ; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [[UITableViewCell alloc] init]; // 设置cell右边的指示样式 // accessoryView的优先级 > accessoryType // cell.accessoryView = [[UISwitch alloc] init]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; ) { // 第0组 ) { // 第0组的第0行 cell.textLabel.text = @"奔驰"; cell.imageView.image = [UIImage imageNamed:@"m_2_100"]; } ) { // 第0组的第1行 cell.textLabel.text = @"宝马"; cell.imageView.image = [UIImage imageNamed:@"m_3_100"]; } } ) { // 第1组 ) { // 第1组的第0行 cell.textLabel.text = @"法拉利"; cell.imageView.image = [UIImage imageNamed:@"m_91_100"]; } ) { // 第1组的第1行 cell.textLabel.text = @"兰博基尼"; cell.imageView.image = [UIImage imageNamed:@"m_86_100"]; } ) { // 第1组的第2行 cell.textLabel.text = @"玛莎拉蒂"; cell.imageView.image = [UIImage imageNamed:@"m_93_100"]; } } return cell; } /** * 告诉tableView第section组的头部标题文字 */ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { ) return @"德系品牌"; return @"意系品牌"; } /** * 告诉tableView第section组的尾部标题文字 */ - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { ) return @"德系品牌NBNBNBN"; return @"意系品牌HAHAHHAHAH"; }
这种方法一般是对初学者来说拿来练手的,真正开发中基本上没有人会这么做,虽然可读性好,但是你不觉得很傻逼吗,拷贝-粘贴-再拷贝-再粘贴。如果我们还有其他地方要用到这里的或者想要拓展这个TableView那你就等着手软吧!
实际开发中我们一半使用的是MVC,这样不仅拓展性好,而且能够很简单的实现想要的功能,最主要是解藕(这是一个很关键的地方),不多说了,直接上代码:
/***
- 1:结合模型使用
- 2:加载plist数据,并且转成模型使用
- 3:多层模型的转换
- 4:模型数据的读取
*/
1:创建一个模型类(子)
创建对应的模型属性
@interface XMGCar : NSObject /** 名字 */ @property (nonatomic, copy) NSString *name; /** 图标 */ @property (nonatomic, copy) NSString *icon; + (instancetype)carWithName:(NSString *)name icon:(NSString *)icon; + (instancetype)carWithDict:(NSDictionary *)dict; @end
实现模型属性中创建模型的方法:
#import "XMGCar.h" @implementation XMGCar + (instancetype)carWithName:(NSString *)name icon:(NSString *)icon { XMGCar *car = [[self alloc] init]; car.name = name; car.icon = icon; return car; } + (instancetype)carWithDict:(NSDictionary *)dict { XMGCar *car = [[self alloc] init]; car.name = dict[@"name"]; car.icon = dict[@"icon"]; return car; } @end
2:创建分组模型类:
穿件对应的模型属性:
#import <Foundation/Foundation.h> @interface XMGCarGroup : NSObject /** 头部 */ @property (nonatomic, copy) NSString *header; /** 尾部 */ @property (nonatomic, copy) NSString *footer; /** 这组的所有车型(数组中存放着XMGCar模型) */ @property (nonatomic, strong) NSArray *cars; + (instancetype)carGroupWithDict:(NSDictionary *)dict; @end
实现创建对应模型的方法:导入子模型(这里需要注意的是怎么去取子模型)
#import "XMGCarGroup.h" #import "XMGCar.h" @implementation XMGCarGroup + (instancetype)carGroupWithDict:(NSDictionary *)dict { XMGCarGroup *group = [[self alloc] init]; group.header = dict[@"header"]; group.footer = dict[@"footer"]; // 将字典数组(装着车的字典) -> 模型数据(装着车的模型) NSMutableArray *cars = [NSMutableArray array]; for (NSDictionary *carDict in dict[@"cars"]) { [cars addObject:[XMGCar carWithDict:carDict]]; } group.cars = cars; return group; } @end
3:控制器实现:
导入两个模型类,创建一个数组用来存放对应的模型数据
使用懒加载从plist文件中加载数据并且转成模型数据
- (NSArray *)carGroups { if (!_carGroups) { // 加载字典数组 NSArray *carGroupDictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"cars" ofType:@"plist"]]; // 创建模型数组 NSMutableArray *carGroupArray = [NSMutableArray array]; // 将字典数组 -> 模型数组 for (NSDictionary *carGroupDict in carGroupDictArray) { XMGCarGroup *carGroup = [XMGCarGroup carGroupWithDict:carGroupDict]; [carGroupArray addObject:carGroup]; } _carGroups = carGroupArray; } return _carGroups; }
你也可以直接使用最傻逼的方法来转模型,但是这种不推荐使用,虽然可以,但是这样离大神就差的有点远了:
- (NSArray *)carGroups { if (!_carGroups) { // 德系品牌 XMGCarGroup *group0 = [[XMGCarGroup alloc] init]; group0.header = @"德系品牌"; group0.footer = @"德系品牌NBNBNNBNBNBN"; group0.cars = @[ [XMGCar carWithName:@"奔驰" icon:@"m_2_100"], [XMGCar carWithName:@"宝马" icon:@"m_3_100"] ]; // 意系品牌 XMGCarGroup *group1 = [[XMGCarGroup alloc] init]; group1.header = @"意系品牌"; group1.footer = @"意系品牌LLLLLLLLL"; group1.cars = @[ [XMGCar carWithName:@"法拉利" icon:@"m_91_100"], [XMGCar carWithName:@"兰博基尼" icon:@"m_86_100"], [XMGCar carWithName:@"玛莎拉蒂" icon:@"m_93_100"] ]; // 日系品牌 XMGCarGroup *group2 = [[XMGCarGroup alloc] init]; group2.header = @"日系品牌"; group2.footer = @"日系品牌KKKKKKKKK"; group2.cars = @[ [XMGCar carWithName:@"丰田" icon:@"m_7_100"], [XMGCar carWithName:@"本田" icon:@"m_26_100"] ]; _carGroups = @[group0, group1, group2]; } return _carGroups; }
实现响应的代理方法
#pragma mark - <UITableViewDataSource> - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return self.carGroups.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { XMGCarGroup *carGroup = self.carGroups[section]; return carGroup.cars.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [[UITableViewCell alloc] init]; // 设置cell右边的指示样式 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // 取出indexPath位置对应的XMGCar模型 XMGCarGroup *carGroup = self.carGroups[indexPath.section]; XMGCar *car = carGroup.cars[indexPath.row]; cell.textLabel.text = car.name; cell.imageView.image = [UIImage imageNamed:car.icon]; return cell; }
最后实现头部和尾部数据的显示:
/** * 告诉tableView第section组的头部标题文字 */ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { XMGCarGroup *carGroup = self.carGroups[section]; return carGroup.header; } /** * 告诉tableView第section组的尾部标题文字 */ - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { XMGCarGroup *carGroup = self.carGroups[section]; return carGroup.footer; } #pragma mark - <UITableViewDelegate> - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { return [UIButton buttonWithType:UIButtonTypeInfoDark]; } - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { return [UIButton buttonWithType:UIButtonTypeContactAdd]; }
/**********************************UItableView结合MVC使用:开发最常用**********************************************/
1:Model
定义模型属性:
#import <Foundation/Foundation.h> @interface XMGTg : NSObject /** 标题 */ @property (nonatomic, copy) NSString *title; /** 购买数 */ @property (nonatomic, copy) NSString *buyCount; /** 图片 */ @property (nonatomic, copy) NSString *icon; /** 价格 */ @property (nonatomic, copy) NSString *price; + (instancetype)tgWithDict:(NSDictionary *)dict; @end
实现模型方法:
#import "XMGTg.h" @implementation XMGTg + (instancetype)tgWithDict:(NSDictionary *)dict { XMGTg *tg = [[self alloc] init]; [tg setValuesForKeysWithDictionary:dict]; return tg; } @end
注:这里直接使用ivc中的方法setValuesForKeysWithDictionary是因为plist中的数据和我们需要的或者定义的事一一对应的,
2:View
通过模型创建一个属性:
#import <UIKit/UIKit.h> @class XMGTg; @interface XMGTgCell : UITableViewCell /** 团购模型数据 */ @property (nonatomic, strong) XMGTg *tg; @end 导入模型类,在私有拓展中定义我们需要用来显示数据的控件 /** 图片 */ @property (nonatomic, weak) UIImageView *iconImageView; /** 标题 */ @property (nonatomic, weak) UILabel *titleLabel; /** 价格 */ @property (nonatomic, weak) UILabel *priceLabel; /** 购买数 */ @property (nonatomic, weak) UILabel *buyCountLabel;
根据前面UIScrollView中学习的自定义View类似实现响应的方法和需要实现的代码:
这里因为是UITableView所以,我们使用的是initWithStyle:reuseIdentifier来初始化控件儿而不是initWithFrame,最后面的reuseIdentifier重用标志,后面讲到UItableView重用机制就明白了。
初始化对应的控件
/** * 在这个方法中添加所有的子控件 */ - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { // 图片 UIImageView *iconImageView = [[UIImageView alloc] init]; [self.contentView addSubview:iconImageView]; self.iconImageView = iconImageView; // 标题 UILabel *titleLabel = [[UILabel alloc] init]; [self.contentView addSubview:titleLabel]; self.titleLabel = titleLabel; // 价格 UILabel *priceLabel = [[UILabel alloc] init]; priceLabel.font = [UIFont systemFontOfSize:]; priceLabel.textColor = [UIColor orangeColor]; [self.contentView addSubview:priceLabel]; self.priceLabel = priceLabel; // 购买数 UILabel *buyCountLabel = [[UILabel alloc] init]; buyCountLabel.font = [UIFont systemFontOfSize:]; buyCountLabel.textColor = [UIColor grayColor]; buyCountLabel.textAlignment = NSTextAlignmentRight; [self.contentView addSubview:buyCountLabel]; self.buyCountLabel = buyCountLabel; } return self; }
计算所有子控件的Frame:
// 通过initWithStyle:创建cell,就不会调用这个方法
//- (instancetype)initWithFrame:(CGRect)frame; /** * 在这个方法中计算所有子控件的frame */ - (void)layoutSubviews { [super layoutSubviews]; CGFloat margin = ; // CGFloat contentH = self.contentView.frame.size.height; // CGFloat contentW = self.contentView.frame.size.width; CGFloat contentH = CGRectGetHeight(self.contentView.frame); CGFloat contentW = CGRectGetWidth(self.contentView.frame); // 图片 CGFloat iconX = margin; CGFloat iconY = margin; CGFloat iconW = ; CGFloat iconH = contentH - * iconY; self.iconImageView.frame = CGRectMake(iconX, iconY, iconW, iconH); // 标题 // CGFloat titleX = iconX + iconW + margin; CGFloat titleX = CGRectGetMaxX(self.iconImageView.frame) + margin; CGFloat titleY = iconY; CGFloat titleW = contentW - titleX - margin; CGFloat titleH = ; self.titleLabel.frame = CGRectMake(titleX, titleY, titleW, titleH); // 价格 CGFloat priceX = titleX; CGFloat priceH = ; // CGFloat priceY = iconY + iconH - priceH; CGFloat priceY = CGRectGetMaxY(self.iconImageView.frame) - priceH; CGFloat priceW = ; self.priceLabel.frame = CGRectMake(priceX, priceY, priceW, priceH); // 购买数 CGFloat buyCountW = ; CGFloat buyCountH = ; CGFloat buyCountX = contentW - margin - buyCountW; // CGFloat buyCountY = iconY + iconH - buyCountH; CGFloat buyCountY = CGRectGetMaxY(self.iconImageView.frame) - buyCountH; self.buyCountLabel.frame = CGRectMake(buyCountX, buyCountY, buyCountW, buyCountH); }
从模型中获取响应的数据放到对应的控件上面显示:
- (void)setTg:(XMGTg *)tg { _tg = tg; self.iconImageView.image = [UIImage imageNamed:tg.icon]; self.titleLabel.text = tg.title; self.priceLabel.text = [NSString stringWithFormat:@"¥%@", tg.price]; self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", tg.buyCount]; }
使用纯代码结合Masonry来初始化Cell:
CGFloat margin = ; // 图片 UIImageView *iconImageView = [[UIImageView alloc] init]; [self.contentView addSubview:iconImageView]; self.iconImageView = iconImageView; [iconImageView makeConstraints:^(MASConstraintMaker *make) { make.left.top.equalTo(self.contentView).offset(margin); make.bottom.equalTo(self.contentView).offset(-margin); make.width.equalTo(); }]; // 标题 UILabel *titleLabel = [[UILabel alloc] init]; [self.contentView addSubview:titleLabel]; self.titleLabel = titleLabel; [titleLabel makeConstraints:^(MASConstraintMaker *make) { make.top.equalTo(iconImageView); make.left.equalTo(iconImageView.right).offset(margin); make.height.equalTo(); make.right.equalTo(self.contentView).offset(-margin); }]; // 价格 UILabel *priceLabel = [[UILabel alloc] init]; priceLabel.font = [UIFont systemFontOfSize:]; priceLabel.textColor = [UIColor orangeColor]; [self.contentView addSubview:priceLabel]; self.priceLabel = priceLabel; [priceLabel makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(titleLabel); make.bottom.equalTo(iconImageView); make.size.equalTo(CGSizeMake(, )); }]; // 购买数 UILabel *buyCountLabel = [[UILabel alloc] init]; buyCountLabel.font = [UIFont systemFontOfSize:]; buyCountLabel.textColor = [UIColor grayColor]; buyCountLabel.textAlignment = NSTextAlignmentRight; [self.contentView addSubview:buyCountLabel]; self.buyCountLabel = buyCountLabel; [buyCountLabel makeConstraints:^(MASConstraintMaker *make) { make.bottom.equalTo(iconImageView); make.right.equalTo(titleLabel); make.size.equalTo(CGSizeMake(, )); }];
3:Controller
上面完成之后,那么控制器里面就很简单了,我们只需要实现少量的代码就能实现响应的功能,这里很好的做到了解藕的功能(数据与逻辑分离)
读取plist转成模型:
- (NSArray *)tgs { if (!_tgs) { // 加载字典数组 NSArray *dictArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"tgs" ofType:@"plist"]]; // 创建模型数组 NSMutableArray *tgArray = [NSMutableArray array]; // 将字典数组 -> 模型数组 for (NSDictionary *dict in dictArray) { XMGTg *tg = [XMGTg tgWithDict:dict]; [tgArray addObject:tg]; } _tgs = tgArray; } return _tgs; }
在ViewDidLoad中注册一个Cell并且设置他的标志符,
self.tableView.rowHeight = ; [self.tableView registerClass:[XMGTgCell class] forCellReuseIdentifier:ID];
实现响应的代理方法:
#pragma mark - <数据源方法> - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.tgs.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 访问缓存池 XMGTgCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 设置数据(传递模型数据) cell.tg = self.tgs[indexPath.row]; return cell; }
/**********************************************************************使用Xib完整自定义Cell**********************************************************************************/
如果是使用Xib的话,那么我们在自定义View里面实现的代码就很少了,不需要layoutSubviews也不需要initWithStyle,只需要实现Settet方法来获取模型数据就可以:
#import "XMGTgCell.h" #import "XMGTg.h" @interface XMGTgCell() @property (weak, nonatomic) IBOutlet UIImageView *iconImageView; @property (weak, nonatomic) IBOutlet UILabel *titleLabel; @property (weak, nonatomic) IBOutlet UILabel *priceLabel; @property (weak, nonatomic) IBOutlet UILabel *buyCountLabel; @end @implementation XMGTgCell - (void)setTg:(XMGTg *)tg { _tg = tg; self.iconImageView.image = [UIImage imageNamed:tg.icon]; self.titleLabel.text = tg.title; self.priceLabel.text = [NSString stringWithFormat:@"¥%@", tg.price]; self.buyCountLabel.text = [NSString stringWithFormat:@"%@人已购买", tg.buyCount]; } @end
同样控制器里面的Nib方法获取对应的View
// 这个方法不会去加载xib文件,因为它是通过alloc 、initWithStyle:..方式创建cell
// [self.tableView registerClass:[XMGTgCell class] forCellReuseIdentifier:ID]; [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([XMGTgCell class]) bundle:nil] forCellReuseIdentifier:ID]; 最后实现代理方法: #pragma mark - <数据源方法> - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.tgs.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { XMGTgCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // // if (!cell) { // cell = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([XMGTgCell class]) owner:nil options:nil] lastObject]; // } cell.tg = self.tgs[indexPath.row]; return cell; }
/******************************************完整自定义UItableView************************************************************/
1:Model
Cell模型:
#import <Foundation/Foundation.h> typedef enum { iCocosSettingItemTypeNone, // 什么也没有 iCocosSettingItemTypeArrow, // 箭头 iCocosSettingItemTypeSwitch // 开关 } iCocosSettingItemType; @interface iCocosSettingItem : NSObject @property (nonatomic, copy) NSString *icon; @property (nonatomic, copy) NSString *title; @property (nonatomic, assign) iCocosSettingItemType type;// Cell的样式 @property (nonatomic, copy) void (^operation)() ; // 点击cell后要执行的操作 + (id)itemWithIcon:(NSString *)icon title:(NSString *)title type:(iCocosSettingItemType)type; @end
实现文件:
#import "iCocosSettingItem.h" @implementation iCocosSettingItem + (id)itemWithIcon:(NSString *)icon title:(NSString *)title type:(iCocosSettingItemType)type { iCocosSettingItem *item = [[self alloc] init]; item.icon = icon; item.title = title; item.type = type; return item; } @end
分组模型:
#import <Foundation/Foundation.h> @interface iCocosSettingGroup : NSObject @property (nonatomic, copy) NSString *header; // 头部标题 @property (nonatomic, copy) NSString *footer; // 尾部标题 @property (nonatomic, strong) NSArray *items; // 中间的条目 @end
这里不需要实现什么,除非你对你的TableView有特别的需求
2:Cell
- 1.新建一个继承自UITableViewCell的类
- 2.重写initWithStyle:reuseIdentifier:方法
添加所有需要显示的子控件(不需要设置子控件的数据和frame, 子控件要添加到contentView中)
进行子控件一次性的属性设置(有些属性只需要设置一次, 比如字体\固定的图片)
3.提供2个模型
- 数据模型: 存放文字数据\图片数据
- frame模型: 存放数据模型\所有子控件的frame\cell的高度
4.cell拥有一个frame模型(不要直接拥有数据模型)
5.重写frame模型属性的setter方法: 在这个方法中设置子控件的显示数据和frame
6.frame模型数据的初始化已经采取懒加载的方式(每一个cell对应的frame模型数据只加载一次)
#import <UIKit/UIKit.h> @class iCocosSettingItem; @interface iCocosSettingCell : UITableViewCell @property (nonatomic, strong) iCocosSettingItem *item; + (id)settingCellWithTableView:(UITableView *)tableView; @end
Cell对应de实现文件:
#import "iCocosSettingCell.h" #import "iCocosSettingItem.h" @interface iCocosSettingCell() { UIImageView *_arrow; UISwitch *_switch; } @end @implementation iCocosSettingCell + (id)settingCellWithTableView:(UITableView *)tableView { // 0.用static修饰的局部变量,只会初始化一次 static NSString *ID = @"Cell"; // 1.拿到一个标识先去缓存池中查找对应的Cell iCocosSettingCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; // 2.如果缓存池中没有,才需要传入一个标识创建新的Cell if (cell == nil) { cell = [[iCocosSettingCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } return cell; } - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { // Initialization code } return self; } - (void)setItem:(iCocosSettingItem *)item { _item = item; // 设置数据 self.imageView.image = [UIImage imageNamed:item.icon]; self.textLabel.text = item.title; if (item.type == iCocosSettingItemTypeArrow) { if (_arrow == nil) { _arrow = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"CellArrow"]]; } // 右边显示箭头 self.accessoryView = _arrow; // 用默认的选中样式 self.selectionStyle = UITableViewCellSelectionStyleBlue; } else if (item.type == iCocosSettingItemTypeSwitch) { if (_switch == nil) { _switch = [[UISwitch alloc] init]; } // 右边显示开关 self.accessoryView = _switch; // 禁止选中 self.selectionStyle = UITableViewCellSelectionStyleNone; } else { // 什么也没有,清空右边显示的view self.accessoryView = nil; // 用默认的选中样式 self.selectionStyle = UITableViewCellSelectionStyleBlue; } } @end
3:Controller
定义一个最基本的TableView用来给其他TableView继承的,这样以后你每实现一个页面活着一个功能只需要继承这个类并且按照使用方法实现响应的代码就可以:
#import <UIKit/UIKit.h> #import "iCocosSettingGroup.h" #import "iCocosSettingItem.h" @interface iCocosBaseSettingViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> { NSMutableArray *_allGroups; // 所有的组模型 } @end
实现相应的代码:
#import "iCocosBaseSettingViewController.h" #import "iCocosSettingCell.h" @interface iCocosBaseSettingViewController () @end @implementation iCocosBaseSettingViewController - (void)loadView { UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame style:UITableViewStyleGrouped]; tableView.delegate = self; tableView.dataSource = self; self.view = tableView; } - (void)viewDidLoad { [super viewDidLoad]; _allGroups = [NSMutableArray array]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return _allGroups.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { iCocosSettingGroup *group = _allGroups[section]; return group.items.count; } #pragma mark 每当有一个cell进入视野范围内就会调用,返回当前这行显示的cell - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 1.创建一个iCocosSettingCell iCocosSettingCell *cell = [iCocosSettingCell settingCellWithTableView:tableView]; // 2.取出这行对应的模型(iCocosSettingItem) iCocosSettingGroup *group = _allGroups[indexPath.section]; cell.item = group.items[indexPath.row]; return cell; } #pragma mark 点击了cell后的操作 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 0.取出这行对应的模型 iCocosSettingGroup *group = _allGroups[indexPath.section]; iCocosSettingItem *item = group.items[indexPath.row]; // 1.取出这行对应模型中的block代码 if (item.operation) { // 执行block item.operation(); } } #pragma mark 返回每一组的header标题 - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { iCocosSettingGroup *group = _allGroups[section]; return group.header; } #pragma mark 返回每一组的footer标题 - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section { iCocosSettingGroup *group = _allGroups[section]; return group.footer; } @end
那么使用起来就非常简单了,
只需要创建一个继承自上面定义的TableView并且直接使用里面的方法来创建对应的Cell和相应的操作:
我们这里以一个设置界面为例,如果你需要做成其他界面,除了空间那种说说界面,基本可以实现其他任何关于TableView界面的现实
#import "iCocosSettingViewController.h" #import "iCocosPushNoticeViewController.h" @interface iCocosSettingViewController () @end @implementation iCocosSettingViewController - (void)viewDidLoad { [super viewDidLoad]; // 1.第0组:3个 [self add0SectionItems]; // 2.第1组:6个 [self add1SectionItems]; } #pragma mark 添加第0组的模型数据 - (void)add0SectionItems { // 1.标题 self.title = @"设置"; // 2.添加左边的按钮 self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStyleDone target:self action:@selector(close)]; // 2.添加左边的按钮 self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"更多" style:UIBarButtonItemStyleDone target:self action:@selector(more)]; // 1.1.推送和提醒 iCocosSettingItem *push = [iCocosSettingItem itemWithIcon:@"MorePush" title:@"推送和提醒" type:iCocosSettingItemTypeArrow]; push.operation = ^{ iCocosPushNoticeViewController *notice = [[iCocosPushNoticeViewController alloc] init]; [self.navigationController pushViewController:notice animated:YES]; }; // 1.2.摇一摇机选 iCocosSettingItem *shake = [iCocosSettingItem itemWithIcon:@"handShake" title:@"摇一摇机选" type:iCocosSettingItemTypeSwitch]; // 1.3.声音效果 iCocosSettingItem *sound = [iCocosSettingItem itemWithIcon:@"sound_Effect" title:@"声音效果" type:iCocosSettingItemTypeSwitch]; iCocosSettingGroup *group = [[iCocosSettingGroup alloc] init]; group.header = @"基本设置"; group.items = @[push, shake, sound]; [_allGroups addObject:group]; } - (void)close { [self dismissViewControllerAnimated:YES completion:nil]; } - (void)more { UITableViewController *tab = [[UITableViewController alloc] init]; [self.navigationController pushViewController:tab animated:YES]; } #pragma mark 添加第1组的模型数据 - (void)add1SectionItems { // 2.1.检查新版本 iCocosSettingItem *update = [iCocosSettingItem itemWithIcon:@"MoreUpdate" title:@"检查新版本" type:iCocosSettingItemTypeArrow]; update.operation = ^{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提醒" message:@"暂时没有新版本" delegate:nil cancelButtonTitle:@"好的" otherButtonTitles:nil]; [alert show]; }; // 2.2.帮助 iCocosSettingItem *help = [iCocosSettingItem itemWithIcon:@"MoreHelp" title:@"帮助" type:iCocosSettingItemTypeArrow]; help.operation = ^{ UIViewController *helpVc = [[UIViewController alloc] init]; helpVc.view.backgroundColor = [UIColor brownColor]; helpVc.title = @"帮助"; [self.navigationController pushViewController:helpVc animated:YES]; }; // 2.3.分享 iCocosSettingItem *share = [iCocosSettingItem itemWithIcon:@"MoreShare" title:@"分享" type:iCocosSettingItemTypeArrow]; // 2.4.查看消息 iCocosSettingItem *msg = [iCocosSettingItem itemWithIcon:@"MoreMessage" title:@"查看消息" type:iCocosSettingItemTypeArrow]; // 2.5.产品推荐 iCocosSettingItem *product = [iCocosSettingItem itemWithIcon:@"MoreNetease" title:@"产品推荐" type:iCocosSettingItemTypeArrow]; // 2.6.关于 iCocosSettingItem *about = [iCocosSettingItem itemWithIcon:@"MoreAbout" title:@"关于" type:iCocosSettingItemTypeArrow]; iCocosSettingGroup *group = [[iCocosSettingGroup alloc] init]; group.header = @"高级设置"; group.footer = @"这的确是高级设置!!!"; group.items = @[update, help, share, msg, product, about]; [_allGroups addObject:group]; } @end
再来一个,直接使用,点击推送和提醒弹出对应的控制器并且实现你想要功能:
#import "iCocosPushNoticeViewController.h" @interface iCocosPushNoticeViewController () @end @implementation iCocosPushNoticeViewController - (void)viewDidLoad { [super viewDidLoad]; self.title = @"推送和提醒"; // 1.1 iCocosSettingItem *item1 = [iCocosSettingItem itemWithIcon:nil title:@"开奖号码推送" type:iCocosSettingItemTypeArrow]; // 1.2 iCocosSettingItem *item2 = [iCocosSettingItem itemWithIcon:nil title:@"中奖动画" type:iCocosSettingItemTypeArrow]; // 1.3 iCocosSettingItem *item3 = [iCocosSettingItem itemWithIcon:nil title:@"比分直播提醒" type:iCocosSettingItemTypeArrow]; // 1.4 iCocosSettingItem *item4 = [iCocosSettingItem itemWithIcon:nil title:@"购彩定时提醒" type:iCocosSettingItemTypeArrow]; iCocosSettingGroup *group = [[iCocosSettingGroup alloc] init]; group.items = @[item1, item2, item3, item4]; [_allGroups addObject:group]; } @end
/********************************************************关于一些UItableView其他使用技巧******************************************************************/
常见属性与方法:
属性:
属性:
/** * 设置数据 */ //图片 cell.imageView.image = [UIImage imageNamed:wine.image]; //文字 cell.textLabel.text = wine.name; //细节文字 cell.detailTextLabel.text = [NSString stringWithFormat:@"¥%@", wine.money]; //图片文字颜色 cell.detailTextLabel.textColor = [UIColor orangeColor]; //图片右边View的类型 cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // 设置cell的选中样式 cell.selectionStyle = UITableViewCellSelectionStyleNone; // backgroundView优先级 > backgroundColor // 设置背景色 cell.backgroundColor = [UIColor redColor]; // 设置背景view UIView *bg = [[UIView alloc] init]; bg.backgroundColor = [UIColor blueColor]; cell.backgroundView = bg; // 设置选中的背景view UIView *selectedBg = [[UIView alloc] init]; selectedBg.backgroundColor = [UIColor purpleColor]; cell.selectedBackgroundView = selectedBg; // 设置背景色 cell.backgroundColor = [UIColor redColor]; // 设置每一行cell的高度 self.tableView.rowHeight = ; // 设置每一组头部的高度 self.tableView.sectionHeaderHeight = ; // 设置每一组尾部的高度 self.tableView.sectionFooterHeight = ; // 设置分割线颜色 self.tableView.separatorColor = [UIColor redColor]; // 设置分割线样式 self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // 设置表头控件 self.tableView.tableHeaderView = [[UISwitch alloc] init]; // 设置表尾控件 self.tableView.tableFooterView = [UIButton buttonWithType:UIButtonTypeContactAdd]; // 设置右边索引文字的颜色 self.tableView.sectionIndexColor = [UIColor redColor]; // 设置右边索引文字的背景色 self.tableView.sectionIndexBackgroundColor = [UIColor blackColor]; // 尽量将cell的初始化设置,放在这个代码块中 // 如果这个设置是所有cell都要保持一致的,就可以放在这个代码块中 cell.textLabel.font = [UIFont systemFontOfSize:]; cell.backgroundColor = [UIColor redColor];
方法:
左滑删除:
#pragma mark - <代理方法> /** * 只要实现了这个方法,左滑出现Delete按钮的功能就有了 * 点击了“左滑出现的Delete按钮”会调用这个方法 */ - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { // 删除模型 [self.wineArray removeObjectAtIndex:indexPath.row]; // 刷新 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft]; } /** * 修改Delete按钮文字为“删除” */ - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { return @"删除"; // return [NSString stringWithFormat:@"删除-%zd", indexPath.row]; }
编辑模式:
#pragma mark - 数据刷新操作 - (IBAction)remove { // self.tableView.editing = !self.tableView.isEditing; [self.tableView setEditing:!self.tableView.isEditing animated:YES]; } #pragma mark - <代理方法> /** * 只要实现了这个方法,左滑出现Delete按钮的功能就有了 * 点击了“左滑出现的Delete按钮”会调用这个方法 */ - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { // 删除模型 [self.wineArray removeObjectAtIndex:indexPath.row]; // 刷新 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft]; } /** * 修改Delete按钮文字为“删除” */ - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { return @"删除"; }
左滑出现更多按钮或者功能:
/** * 左滑cell时出现什么按钮 */ - (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewRowAction *action0 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:@"关注" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { NSLog(@"点击了关注"); // 收回左滑出现的按钮(退出编辑模式) tableView.editing = NO; }]; UITableViewRowAction *action1 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"删除" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) { [self.wineArray removeObjectAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; }]; return @[action1, action0]; }
多选并且删除:
// 编辑模式的时候可以多选 self.tableView.allowsMultipleSelectionDuringEditing = YES; // 隐藏删除按钮 self.removeButton.hidden = YES; #pragma mark - 数据刷新操作 - (IBAction)multipleRemove { [self.tableView setEditing:!self.tableView.isEditing animated:YES]; self.removeButton.hidden = !self.tableView.isEditing; } - (IBAction)remove { // self.tableView.indexPathsForSelectedRows = [0, 1] // 获得需要删除的酒模型数据 NSMutableArray *deletedWineArray = [NSMutableArray array]; for (NSIndexPath *indexPath in self.tableView.indexPathsForSelectedRows) { [deletedWineArray addObject:self.wineArray[indexPath.row]]; } // 删除模型数据 [self.wineArray removeObjectsInArray:deletedWineArray]; // 刷新 [self.tableView deleteRowsAtIndexPaths:self.tableView.indexPathsForSelectedRows withRowAnimation:UITableViewRowAnimationLeft]; }
#pragma mark - <代理方法>
/**
* 只要实现了这个方法,左滑出现Delete按钮的功能就有了
* 点击了“左滑出现的Delete按钮”会调用这个方法
*/
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { // 删除模型 [self.wineArray removeObjectAtIndex:indexPath.row]; // 刷新 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationLeft]; } /** * 修改Delete按钮文字为“删除” */ - (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath { return @"删除"; }
刷新界面:
1局部刷新
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationRight]; [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationMiddle]; [self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationLeft];
2:全局刷新
[self.tableView reloadData];
其他技巧:---------------------
分组并且实现隐藏:
tableView显示和隐藏
@property(nonatomic, assign)BOOL isHiddenItem; self.isHiddenItem = NO; - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { // 把要隐藏的item的高度设置为0 if (self.isHiddenItem) { ; } ; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ) { // 标示是否被隐藏 self.isHiddenItem = YES; // 获取要隐藏item的位置 NSIndexPath *tmpPath = [NSIndexPath indexPathForRow:indexPath.row + inSection:indexPath.section]; [UIView animateWithDuration:0.3 animations:^{ [self.tableView cellForRowAtIndexPath:tmpPath].alpha = 0.0f; } completion:^(BOOL finished) { // 隐藏的对应item [[self.tableView cellForRowAtIndexPath:tmpPath] setHidden:YES]; // 刷新被隐藏的item [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:tmpPath] withRowAnimation:UITableViewRowAnimationFade]; }]; NSLog(@"点击了第0行"); } ){ self.isHiddenItem = NO; NSIndexPath *tmpPath = [NSIndexPath indexPathForRow:indexPath.row + inSection:indexPath.section]; [UIView animateWithDuration:0.3 animations:^{ [self.tableView cellForRowAtIndexPath:tmpPath].alpha = 1.0f; } completion:^(BOOL finished) { [[self.tableView cellForRowAtIndexPath:tmpPath] setHidden:YES]; [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:tmpPath] withRowAnimation:UITableViewRowAnimationFade]; }]; NSLog(@"点击了第1行"); } }
/*************************Swift:UITableView简单实现**********************************************/
使用UIViewController创建TableView
1:在代理中的application:didFinishLaunchingWithOptions方法中初始化一个UIview控制器,并且将他加到导航控制器中,最后将导航控制器作为主窗口显示到界面上
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool { var vc = ViewController() var nav = UINavigationController(rootViewController:vc); var rect = UIScreen.mainScreen().bounds; self.window = UIWindow(frame: rect); self.window!.rootViewController = nav; self.window?.makeKeyAndVisible(); return true }
2:创建一个UIViewController控制器,遵守协议:,UITableViewDelegate, UITableViewDataSource
3:初始化界面:
var tableView : UITableView? var items :NSMutableArray? var leftBtn:UIButton? override func viewDidLoad() { super.viewDidLoad() self.title = "I love Swift" self.items = NSMutableArray() // self.items?.addObject("1","2") // Do any additional setup after loading the view, typically from a nib. setupViews() setupRightBarButtonItem() setupLeftBarButtonItem(); } func setupViews() { self.tableView = UITableView(frame:self.view!.frame) self.tableView!.delegate = self self.tableView!.dataSource = self self.tableView!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell") self.view?.addSubview(self.tableView!) } func setupLeftBarButtonItem() { self.leftBtn = UIButton.buttonWithType(UIButtonType.Custom) as? UIButton self.leftBtn!.frame = CGRectMake(,,,) self.leftBtn?.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal) self.leftBtn?.setTitle("Edit", forState: UIControlState.Normal) self.leftBtn!.tag = self.leftBtn!.userInteractionEnabled = false self.leftBtn?.addTarget(self, action: "leftBarButtonItemClicked", forControlEvents: UIControlEvents.TouchUpInside) var barButtonItem = UIBarButtonItem(customView: self.leftBtn!) self.navigationItem.leftBarButtonItem = barButtonItem } func setupRightBarButtonItem() { var barButtonItem = UIBarButtonItem(title: "Add", style: UIBarButtonItemStyle.Plain, target: self, action: "rightBarButtonItemClicked") self.navigationItem.rightBarButtonItem = barButtonItem } func rightBarButtonItemClicked() { var row = self.items!.count var indexPath = NSIndexPath(forRow:row,inSection:) self.items?.addObject(") self.tableView?.insertRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Left) self.leftBtn!.userInteractionEnabled = true } func leftBarButtonItemClicked() { ) { self.tableView?.setEditing(true, animated: true) self.leftBtn!.tag = self.leftBtn?.setTitle("Done", forState: UIControlState.Normal) } else { self.tableView?.setEditing(false, animated: true) self.leftBtn!.tag = self.leftBtn?.setTitle("Edit", forState: UIControlState.Normal) }
}
4:实现必须实现的三个数据源方法
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.items!.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! UITableViewCell cell.textLabel!.text = String(format: ) return cell }
5:点击Add就可以实现在界面上添加行:
6:选中每一行的时候显示灰色:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { println("row = %d",indexPath.row) }
7:我们可以实现时按住一行不动然后实现移动
8:点击左边的Edit按钮可是实现编辑每一行:
func tableView(tableView: UITableView,canMoveRowAtIndexPath indexPath: NSIndexPath)-> Bool { return true } func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) { self.tableView?.moveRowAtIndexPath(sourceIndexPath, toIndexPath: destinationIndexPath) self.items?.exchangeObjectAtIndex(sourceIndexPath.row, withObjectAtIndex: destinationIndexPath.row) }
9:并且可手指往左拖动可以实现删除(编辑)对应的一行:
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { return true } func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { self.items?.removeObjectAtIndex(indexPath.row) self.tableView?.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Top) ) { self.leftBtn!.userInteractionEnabled = false } } func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle { return (UITableViewCellEditingStyle.Delete) }