自定义cell
概要:
自定义cell就是创建一个UITableViewCell的子类。
把cell上的控件创建都封装在子类中,简化UIViewController中的代 码。
子视图控件添加到cell的contentView上。
通信
cell中声明一个Model类型的属性,viewController中获取到Model对象后赋值给cell的Model属性。
cell中重写Model的setter方法,把Model对象中的内容重新赋值给各
个控件 M和V不直接进行通信,C负责M和V之间进行通信。
创建新工程,添加加载数据方法和创建TableView
// 加载数据
- (void)setUpData
{
// 获取文件路径
NSString *path = [[NSBundle mainBundle] pathForResource:@"StudentList" ofType:@"plist"];
// 从该路径 读取文件
NSArray *arr = [NSArray arrayWithContentsOfFile:path];
// 字典转化成模型 再放入字典或数组
self.dataArr = [NSMutableArray array];
for (NSDictionary *dic in arr) {
CellModel *model = [[CellModel alloc] init];
[model setValuesForKeysWithDictionary:dic];
[self.dataArr addObject:model];
[model release];
}
}
- (void)addTableView
{
UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
[tableView release];
}
自定义cell的步骤:
创建TableViewCell的子类
创建2个名为MyTableViewCell、ManTableViewCell的继承于TableViewCell的子类
重写初始化方法
MyTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self addSubView];
}
return self;
}
ManTableViewCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self addSubview];
}
return self;
}
把要添加的控件 添加在cell的实现内容区域 contentView上面
MyTableViewCell
// 添加子视图
- (void)addSubView
{
self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(kMargin, kMargin, kImageWidth, kImageHeight)];
self.imageV.backgroundColor = [UIColor redColor];
[self.contentView addSubview:self.imageV];
[self.imageV release];
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.imageV.right + kMargin, self.imageV.top, (kScreenWidth - 3*kMargin - self.imageV.width), (kImageHeight - 2*kLabelMargin)/3)];
self.nameLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.nameLabel];
[self.nameLabel release];
self.phoneLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.nameLabel.left, self.nameLabel.bottom + kLabelMargin, self.nameLabel.width, self.nameLabel.height)];
self.phoneLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.phoneLabel];
[self.phoneLabel release];
self.genderLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.phoneLabel.left, self.phoneLabel.bottom + kLabelMargin, self.phoneLabel.width, self.phoneLabel.height)];
self.genderLabel.backgroundColor = [UIColor greenColor];
[self.contentView addSubview:self.genderLabel];
[self.genderLabel release];
}
ManTableViewCell
- (void)addSubview
{
self.nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
self.nameLabel.backgroundColor = [UIColor orangeColor];
[self.contentView addSubview:self.nameLabel];
[self.nameLabel release];
}
把系统的cell 替换成 自定义cell完成
MyTableViewCell
static NSString *identifier = @"GirlCell";
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
ManTableViewCell
static NSString *identifier = @"ManCell";
ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
多种类型的cell混合使用
注意:
通常我们会在tableView:cellForRowAtIndexPath:方法中根据不同的。
Model来决定使用什么类型的cell 每种类型的cell要定义不同的重用标识符 。
cell重用的时候会根据重用标识从重用队列中取用哪种类型的cell。
新建一个cellModel继承于NSObject,声明它的属性
// 名字
@property (nonatomic, retain) NSString *name;
// 性别
@property (nonatomic, retain) NSString *gender;
// 手机号
@property (nonatomic, retain) NSString *phoneNumber;
把它声明成tableViewcell子类的属性,见上文截图中。
用model在- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法中判断用哪种cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 根据model的值进行判断 显示不同的cell
CellModel *model = self.dataArr[indexPath.row];
if ([model.gender isEqualToString:@"女"]) {
static NSString *identifier = @"GirlCell";
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[MyTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
}
// 在给model赋值的同时 咱们希望 也给cell上控件完成赋值
cell.model = model;
return cell;
} else {
static NSString *identifier = @"ManCell";
ManTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[ManTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
}
cell.model = model;
return cell;
}
}
这样就能达到下面的效果了
cell自适应高度
获取文本高度:(iOS7)
- (CGRect)boundingRectWithSize:(CGSize)size options:
(NSStringDrawingOptions)options attributes:(NSDictionary
*)attributes context:(NSStringDrawingContext *)context
注意:
计算文本高度是所用的字体要和label显示时用的字体一致。
label的宽度要和计算时使用的限定宽度一致。
这样才能保证文本显示在label中时,label高度恰巧够。
例题:实现如下效果,label正好是适应文字大小
新建工程,创建一个UIlabel
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, 100, 300, 100)];
label.backgroundColor = [UIColor orangeColor];
label.font = [UIFont systemFontOfSize:16];
label.text = @" 我崇拜高尚的生命的秘密。我崇拜这生命在降生、成长、战斗、伤残、牺牲时迸溅出的钢花焰火。我崇拜一个活灵灵的生命在崇山大河,在海洋和大陆上飘荡的自由。是的,生命就是希望。它飘荡无定,自由自在,它使人类中总有一支血脉不甘于失败,九死不悔地追寻着自己的金牧场。";
[self.window addSubview:label];
[label release];
构建字体大小的字典 UIFont,NSFontAttributeName UIFont 在系统中的属性的名字(key)
// 参数size
// 宽度 跟你的label一边宽
// 高度 填一个这段字最大高度
// CGFLOAT_MAX 最大的float数
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:16], NSFontAttributeName, nil];
CGRect frame = [label.text boundingRectWithSize:CGSizeMake(300, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:dic context:nil];
NSLog(@"%f", frame.size.height);
// 更改label的高度
CGRect temp = label.frame;
temp.size.height = frame.size.height;
label.frame = temp;
label.numberOfLines = 0;
[self.window addSubview:label];
[label release];
这样就能简单实现label适应文字的大小了
那怎么让cell自适应高度呢?怎么解决cell的复用问题呢?
例题:
新建工程,添加加载数据方法,添加表视图
- (void)setUpData
{
NSString *path = [[NSBundle mainBundle] pathForResource:@"NewsData" ofType:@"plist"];
NSDictionary *dicData = [NSDictionary dictionaryWithContentsOfFile:path];
self.dataArr = [NSMutableArray array];
NSArray *newsArr = dicData[@"news"];
for (NSDictionary *dic in newsArr) {
NewsModel *model = [[NewsModel alloc] init];
[model setValuesForKeysWithDictionary:dic];
// 给点击状态赋值初值
model.isSelect = NO;
[self.dataArr addObject:model];
[model release];
}
}
- (void)addTableView
{
UITableView *tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:(UITableViewStylePlain)];
tableView.dataSource = self;
tableView.delegate = self;
[self.view addSubview:tableView];
[tableView release];
}
创建NewsModel,声明属性
@property (nonatomic, retain) NSString *title;
@property (nonatomic ,retain) NSString *summary;
// 标识选中的状态
@property (nonatomic, assign) BOOL isSelect;
创建NewsTableViewCell,重写初始化方法,添加视图
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self addSubview];
}
return self;
}
- (void)addSubview
{
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(kMargin, kMargin, kLabelWidth, kLabelHeight)];
self.titleLabel.backgroundColor = [UIColor cyanColor];
[self.contentView addSubview:self.titleLabel];
[self.titleLabel release];
self.summaryLabel = [[UILabel alloc] initWithFrame:CGRectMake(self.titleLabel.left, self.titleLabel.bottom + kMargin, self.titleLabel.width, self.titleLabel.height)];
self.summaryLabel.backgroundColor = [UIColor orangeColor];
// 多行显示
self.summaryLabel.numberOfLines = 0;
// 设置字体大小
self.summaryLabel.font = [UIFont systemFontOfSize:16];
[self.contentView addSubview:self.summaryLabel];
[self.summaryLabel release];
self.imageV = [[UIImageView alloc] initWithFrame:CGRectMake(self.titleLabel.right + kMargin, kMargin, 3 * kMargin, 3 * kMargin)];
[self.contentView addSubview:self.imageV];
[self.imageV release];
}
实现协议中的两个方法
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"MyCell";
NewsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[NewsTableViewCell alloc] initWithStyle:(UITableViewCellStyleSubtitle) reuseIdentifier:identifier] autorelease];
}
NewsModel *model = self.dataArr[indexPath.row];
cell.model = model;
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.dataArr.count;
}
实现cell的点击方法
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 取出点击 cell对应的model
NewsModel *model = self.dataArr[indexPath.row];
// 取出被点击的cell
NewsTableViewCell *cell = (NewsTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
// 更改点击的状态
model.isSelect = !model.isSelect;
if (model.isSelect == YES) {
// 更改cell上的图片
cell.imageV.image = [UIImage imageNamed:@"select"];
} else {
// 更改cell上的图片
cell.imageV.image = [UIImage imageNamed:@"cancel"];
}
}
重写model的setter方法
- (void)setModel:(NewsModel *)model
{
if (_model != model) {
[_model release];
_model = [model retain];
}
self.titleLabel.text = model.title;
self.summaryLabel.text = model.summary;
// 利用model中的点选状态 解决cell复用问题
// 需要每次被复用的cell 再进行一次与状态对应的赋值
if (model.isSelect == YES) {
self.imageV.image = [UIImage imageNamed:@"select"];
} else {
self.imageV.image = [UIImage imageNamed:@"cancel"];
}
// 获取字符串的高度
CGFloat summaryHeight = [NewsTableViewCell cellHeightForModel:model];
// 改变一下label的高度
CGRect frame = self.summaryLabel.frame;
frame.size.height = summaryHeight;
self.summaryLabel.frame = frame;
}
计算高度的类方法
+ (CGFloat)cellHeightForModel:(NewsModel *)model
{
// 创建字体大小的字典
NSDictionary *fontDic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
// 计算字符串高度
CGRect textRect = [model.summary boundingRectWithSize:CGSizeMake(kLabelWidth, CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) attributes:fontDic context:nil];
return textRect.size.height;
}
返回cell的动态高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NewsModel *model = self.dataArr[indexPath.row];
CGFloat labelHeight = [NewsTableViewCell cellHeightForModel:model];
// 上边距 + topLabel + 中间间距 + 动态label高度 + 下边距
return 20 + labelHeight + 40 + 10;
}
这样就解决了cell的自适应高度和cell的复用问题。