前言
上一篇文章总结了通过xib自定义cell。当cell中的内容不定,cell的高度也就不定。此时就要动态计算cell的高度,也就是采用本篇文章要讲的代码自定义cell中的方式。
本篇文章就以Demo中 图书馆->查找书籍 的界面为例
首先看一下我们最终实现的样子
![](https://img.haomeiwen.com/i985173/3005440d8659bbdc.png)
通过代码自定义cell
-
1.新建模型类Query继承自NSObject。Query.h文件中添加cell中对应的数据属性
@property (nonatomic, copy) NSString *image; // 封面
@property (nonatomic, copy) NSString *title; // 书名
@property (nonatomic, copy) NSString *author; // 作者
@property (nonatomic, copy) NSString *publish; // 出版社
@property (nonatomic, copy) NSString *number; // 索书号
@property (nonatomic, copy) NSString *place; // 馆藏地
@property (nonatomic, copy) NSString *desc; // 简介
Query.m中重写init方法
- (id)initWithDict:(NSDictionary *)dict {
if (self = [super init]) {
// 解析字典属性
self.image = dict[@"image"];
self.title = dict[@"title"];
self.author = dict[@"author"];
self.publish = dict[@"publish"];
self.number = dict[@"number"];
self.place = dict[@"place"];
self.desc = dict[@"desc"];
}
return self;
}
并在Query.m中提供一个类方法
+ (id)queryWithDict:(NSDictionary *)dict {
return [[self alloc] initWithDict:dict];
} -
2.新建QueryFrame类继承自UIView。QueryFrame.h文件中添加cell中的属性
@property (nonatomic, copy) NSString *image; // 封面
@property (nonatomic, copy) NSString *title; // 书名
@property (nonatomic, copy) NSString *author; // 作者
@property (nonatomic, copy) NSString *publish; // 出版社
@property (nonatomic, copy) NSString *number; // 索书号
@property (nonatomic, copy) NSString *place; // 馆藏地
@property (nonatomic, copy) NSString *desc; // 简介
并在QueryFrame.h添加另外两个属性
// cellHeight用于计算cell的高度
@property (nonatomic, assign, readonly) CGFloat cellHeight;
// 拥有Query类的属性
@property (nonatomic, strong) Query *query;
在QueryFrame.m中重写setQuery方法,计算各个控件的frame及cell的高度
- (void)setQuery:(Query *)query {
_query = query;// 1.图片 CGFloat imageX = kImageXY; CGFloat imageY = kImageXY; CGFloat imageW = kImageW; CGFloat imageH = kImageH; _imageF = CGRectMake(imageX, imageY, imageW, im ageH); // 2.书名 CGFloat titleX = CGRectGetMaxX(_imageF)+kCellBorder; CGFloat titleY = imageY; CGFloat titleW = 375-titleX-kCellBorder; CGFloat titleH = 41; _titleF = CGRectMake(titleX, titleY, titleW, titleH); // 3.作者 CGFloat authorX = titleX + 46; CGFloat authorY = titleY + titleH + kCellBorder; CGFloat authorW = 189; CGFloat authorH = kLabelH; _authorF = CGRectMake(authorX, authorY, authorW, authorH); // 4.出版社 CGFloat publishX = titleX + 60; CGFloat publishY = CGRectGetMaxY(_authorF) + kCellBorder; CGFloat publishW = 175; CGFloat publishH = kLabelH; _publishF = CGRectMake(publishX, publishY, publishW, publishH); // 5.索书号 CGFloat numberX = imageX + 60; CGFloat numberY = CGRectGetMaxY(_imageF) + kCellBorder; CGFloat numberW = 120; CGFloat numberH = kLabelH; _numberF = CGRectMake(numberX, numberY, numberW, numberH); // 6.馆藏地 CGFloat placeX = CGRectGetMaxX(_numberF)+60; CGFloat placeY = CGRectGetMaxY(_imageF) + kCellBorder; CGFloat placeW = 95; CGFloat placeH = kLabelH; _placeF = CGRectMake(placeX, placeY, placeW, placeH); // 7.简介 CGFloat descX = imageX; CGFloat descY = CGRectGetMaxY(_numberF) + kCellBorder + kLabelH + kCellBorder; CGFloat descW = 375 - 2*imageX; CGSize contentSize = [_query.desc boundingRectWithSize:CGSizeMake(descW, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17]} context:nil].size; _descF = CGRectMake(descX, descY, descW, contentSize.height); // 行高 _cellHeight = descY + contentSize.height + kCellBorder; }
-
3.新建BorrowCell类继承自UITableViewCell
拥有QueryFrame的属性
@property (nonatomic, strong) QueryFrame *queryFrame;
在QueryCell.m中的@interface中声明各个控件的属性
@interface QueryCell () {
// 1.封面
UIImageView *_image;
// 2.书名
UILabel *_title;// 3.“作者” UILabel *_authorLabel; // 3.作者 UILabel *_author; // 4.“出版社” UILabel *_publishLabel; // 4.出版社 UILabel *_publish; // 5.“索书号” UILabel *_numberLabel; // 5.索书号 UILabel *_number; // 6.“馆藏地” UILabel *_placeLabel; // 6.馆藏地 UILabel *_place; // 7.“简介” UILabel *_descLabel; // 7.简介 UILabel *_desc; }
在QueryCell.m中重写initWithStyle: reuseIdentifier:方法(注意:只管创建添加子控件,先不要去管子控件的位置和尺寸)
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// 添加内部的子控件
// 1.封面
_image = [[UIImageView alloc] init];
[self.contentView addSubview:_image];
// 2.书名
_title = [[UILabel alloc] init];
_title.numberOfLines = 0;
[self.contentView addSubview:_title];
// 3.“作者”
CGFloat authorLabelX = kImageXY + kImageW + kCellBorder;
CGFloat authorLabelY = kImageXY + 41 + kCellBorder;
CGFloat authorLabelW = 46;
CGFloat authorLabelH = kLabelH;
_authorLabel = [[UILabel alloc] initWithFrame:CGRectMake(authorLabelX, authorLabelY, authorLabelW, authorLabelH)];
_authorLabel.text = @"作者:";
_authorLabel.font = [UIFont systemFontOfSize:15];
[self.contentView addSubview:_authorLabel];
// 3.作者
_author = [[UILabel alloc] init];
[self.contentView addSubview:_author];
// 4.“出版社”
CGFloat publishLabelX = authorLabelX;
CGFloat publishLabelY = authorLabelY + authorLabelH + kCellBorder;
CGFloat publishLabelW = 60;
CGFloat publishLabelH = kLabelH;
_publishLabel = [[UILabel alloc] initWithFrame:CGRectMake(publishLabelX, publishLabelY, publishLabelW, publishLabelH)];
_publishLabel.text = @"出版社:";
_publishLabel.font = [UIFont systemFontOfSize:15];
[self.contentView addSubview:_publishLabel];
// 4.出版社
_publish = [[UILabel alloc] init];
[self.contentView addSubview:_publish];
// 5.“索书号”
CGFloat numberLabelX = kImageXY;
CGFloat numberLabelY = kImageXY + kImageH + kCellBorder;
CGFloat numberLabelW = 60;
CGFloat numberLabelH = kLabelH;
_numberLabel = [[UILabel alloc] initWithFrame:CGRectMake(numberLabelX, numberLabelY, numberLabelW, numberLabelH)];
_numberLabel.text = @"索书号:";
_numberLabel.font = [UIFont systemFontOfSize:15];
[self.contentView addSubview:_numberLabel];
// 5.索书号
_number = [[UILabel alloc] init];
[self.contentView addSubview:_number];
// 6.“馆藏地”
CGFloat placeLabelX = kImageXY + numberLabelW + 120;
CGFloat placeLabelY = numberLabelY;
CGFloat placeLabelW = 60;
CGFloat placeLabelH = kLabelH;
_placeLabel = [[UILabel alloc] initWithFrame:CGRectMake(placeLabelX, placeLabelY, placeLabelW, placeLabelH)];
_placeLabel.text = @"馆藏地:";
_placeLabel.font = [UIFont systemFontOfSize:15];
[self.contentView addSubview:_placeLabel];
// 6.馆藏地
_place = [[UILabel alloc] init];
[self.contentView addSubview:_place];
// 7.“简介”
CGFloat descX = kImageXY;
CGFloat descY = numberLabelY + numberLabelH + kCellBorder;
CGFloat descW = 80;
CGFloat descH = kLabelH;
_descLabel = [[UILabel alloc] initWithFrame:CGRectMake(descX, descY, descW, descH)];
_descLabel.text = @"图书简介:";
_descLabel.font = [UIFont systemFontOfSize:15];
[self.contentView addSubview:_descLabel];
// 简介
_desc = [[UILabel alloc] init];
_desc.numberOfLines = 0;
[self.contentView addSubview:_desc];
}
return self;
}
注:当存在固定内容时, 在init方法中直接写定内容、计算出高度就好了。而对于需要根据模型数据显示内容的标签暂时alloc,添加到cell的contentView上就好,在后边的setter方法中会计算高度、添加数据。
-
4.重写setQueryFrame方法,并该方法中调用设置数据和设置子控件frame的方法
- (void)setQueryFrame:(QueryFrame *)queryFrame {
_queryFrame = queryFrame;
// 1.设置数据
[self settingData];// 2.设置子控件的frame(x, y, width, height) [self settingSubviewFrame]; }
设置数据的方法
- (void)settingData{
Query *query = _queryFrame.query;
// 封面
NSString *imageStr = [query.image stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:imageStr];
[_image sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"book.png"]];
// 2.书名
_title.text = query.title;
_title.textColor = [UIColor grayColor];
// 3.作者
_author.text = query.author;
_author.textColor = [UIColor grayColor];
// 4.出版社
_publish.text = query.publish;
_publish.textColor = [UIColor grayColor];
// 5.索书号
_number.text = query.number;
_number.textColor = [UIColor grayColor];
// 6.馆藏地
_place.text = query.place;
_place.textColor = [UIColor grayColor];
// 简介
_desc.text = query.desc;
_desc.textColor = [UIColor grayColor];
}
注:settingData方法中若是直接加载封面图片,界面会卡顿。所以这里采用第三方库SDWebImage中的方法,如果不想使用第三方库,也可以采用下面的多线程方法:
// 封面
_image.image = [UIImage imageNamed:@"book.png"]; // 放置一张加载完成前的默认图片
// 1.获取一个全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.把任务添加到队列中执行
dispatch_async(queue, ^{
// 3.从网络上下载图片
NSURL *urlstr = [NSURL URLWithString: query.image];
NSData *data = [NSData dataWithContentsOfURL:urlstr];
UIImage *image = [UIImage imageWithData:data];
// 4.回到主线程,展示图片
dispatch_async(dispatch_get_main_queue(), ^{
_image.image = image;
});
});
设置总控件frame的方法
- (void) settingSubviewFrame {
// 1.封面
_image.frame = _queryFrame.imageF;
// 2.书名
_title.frame = _queryFrame.titleF;
// 3.作者
_author.frame = _queryFrame.authorF;
// 4.出版社
_publish.frame = _queryFrame.publishF;
// 5.索书号
_number.frame = _queryFrame.numberF;
// 6.馆藏地
_place.frame = _queryFrame.placeF;
// 简介
_desc.frame = _queryFrame.descF;
}
QueryViewController中显示的内容
-
1.添加UITableView
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, kLabelY+90, self.view.frame.size.width, self.view.frame.size.height-140) style:UITableViewStyleGrouped];
_tableView.dataSource = self;
_tableView.delegate = self;
[self.view addSubview:_tableView]; -
2.添加本地数据
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Query.plist" ofType:nil]];
_queryFrames = [NSMutableArray array];
for (NSDictionary *dict in array) {
// 创建frame对象
QueryFrame *queryF = [[QueryFrame alloc] init];
queryF.query = [Query queryWithDict:dict];
[_queryFrames addObject:queryF];
} -
4.遵守UITableViewDataSource、UITableViewDelegate
实现UITableViewDataSource方法
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1; // 此方法默认为1,可省略
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _queryFrames.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // 设置cell的重用标识 static NSString *ID = @"cell"; // 到缓存池中根据标识取cell QueryCell *cell = [tableView dequeueReusableHeaderFooterViewWithIdentifier:ID]; // 如果缓存池中没有cell,就用QueryCell创建 if (!cell) { cell = [[QueryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID]; } // 设置cell不能选中 cell.selectionStyle = UITableViewCellSelectionStyleNone; // 传递模型数据 cell.queryFrame = _queryFrames[indexPath.row]; return cell; }
实现UITableViewDelegate方法,计算行高
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [_queryFrames[indexPath.row] cellHeight];
}
如此,遵从了MVC模式,减轻了controller的工作量,交给它的模型去做。并可以根据cell内容计算其高度
不需要计算cell高度时通过代码自定义cell
以上是需要计算cell的高度时才需要新建QueryFrame类。而当cell中控件的位置和尺寸固定时,采用代码方式创建cell时将省去计算cell的高度,要比上边的过程简单一些,这里大概罗列一下步骤:
- 1.新建一个模型类(Query)增加对应的数据属性
- 2.新建一个UITableViewCell的子类(QueryCell)
- 3.在创建cell的时候,重写initWithStyle: reuseIdentifier:方法,添加cell内部需要使用的子控件(注意:只管创建添加子控件,先不要去管控件的位置和尺寸)
- 4.给QueryCell增加一个Query模型属性,在拿到Query模型数据的同时设置子控件的属性
- 5.重写setQuery:方法,在这里取出Query模型的数据显示到子控件上
解释为什么计算cell高度时才需要新建QueryFrame类?
数据源方法与代理方法的调用顺序:
先调用:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
再调用:且总共有多少行cell一次性全部算出
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
最后调用:
- (UITableViewCell )tableView:(UITableView )tableView cellForRowAtIndexPath:(NSIndexPath )indexPath;
QueryCell是到- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; **方法中才调用的,所以,如果在QueryCell的set方法中计算frame是无效的。装有模型数据的数组中的Query并没有cell高度的数据。故需要新建QueryFrame类,拥有cellHeight属性,将其添加到拥有数据模型的数组中。
后记
小白出手,请多指教。
如言有误,还望斧正!
- 转载请保留原文地址:http://www.jianshu.com/p/7db27a3a2231
- Demo的GitHub地址:QLU-BlogDemo
- 有兴趣的读者欢迎关注我的微博:与佳期
网友评论