Emmmm…. 时间过得好快,一晃眼一年就过去了,打开尘封已久的CSDN博客,发现自己堕落了无数时光,但人活着总是要混口饭吃的嘛,所以再度起航,键盘敲起来,文章写起来。希望大家能支持我这个垃圾猿,好啦切入正题。
皮一下很开心
上周在技术群里看萌新们在问UITableView的Cell怎么自适应宽高(xib和storyboard的我就不说了,关东升老哥的书里全都是),有装大佬的人就给他们甩一脸名词过去—-“动态计算高度”,萌新们顿时“哇”,“大佬求带”,“大佬求教” and so on,看得我一脸黑线,我当时就去网上搜所谓的“动态计算高度”,其实然并卵,很多都是互相抄袭、标题党、说的玄乎罢了。
然而事实上,其实都是一样获取到了需要计算的文本、图片等信息,来进行计算,为了提高关注度,加上一个所谓的“动态计算”。而且互相抄袭的实质就是很多人把注释和关键属性、方法的使用方式隐藏了或者抹掉了,这样让那些萌新们觉得:“这就是大佬啊!”—呵呵
进入正题
今天介绍的是UITableview的Cell高度计算方法,这里没有高端的说辞,大佬勿看,萌新请进,不喜勿喷,谢谢合作~
1.首先咱们先来创建一个UITableview
- (void)CreateUI{
UITableView * tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 20, self.view.frame.size.width, self.view.frame.size.height - 20)];
tableView.backgroundColor = [UIColor whiteColor];
tableView.dataSource = self;
tableView.delegate = self;
tableView.separatorColor = [UIColor clearColor];
[self.view addSubview:tableView];
}
2.创建数据源,我们假设数据源为【文字+图片】,以一种很简单的方式呈现出来。
(数据源在本地,有8张图片,文本如以下方法里面的文字)
- (NSMutableArray *)CreateData{
// 数据源
NSMutableArray * array = [[NSMutableArray alloc]init];
// 在获取数据源的时候,一般都会的到图片的宽高,如果没有,我们就用CGSize来获取
for (int i = 0; i < 8; i++) {
NSMutableDictionary * dic = [[NSMutableDictionary alloc]init];
CGSize size = [UIImage imageNamed:[NSString stringWithFormat:@"%d.jpg", i + 1]].size;
// 获取图片宽高,根据宽高比例计算新的宽高
CGFloat imgheight = [self getImagWidth:size.width imageHeight:size.height];
// 根据label宽度、文字字数、文字大小计算新的宽高
CGFloat labheight = [self getWidth:self.view.frame.size.width title:@"按实际佛is南方路口is女离开谁知道呢是;你温柔看;否那我可;老妇女卡洛斯的女警卡拉水泥大V看刘女士的乌克兰那我可老妇女为快乐风男考虑对方是你的快乐发是哪里看的真是女分类看是否离开 萨瓦迪卡嫁女记初中生的的女离开SZN SKNJD距中山东路东南角WE;失地农民了;你WAE法芙娜看来是在哪读离开WWNSE弗兰克SND克鲁苏NDvjklnwaKlfnawkerljnslkvcn只是看了你就开了瓦恩积分klwaNJE lknkldvnklszdnvkldzasn klandgk lnazelkr " font:[UIFont systemFontOfSize:15]];
// 宽度的存入,需要存入目标tableview的cell的宽度
[dic setValue:[NSString stringWithFormat:@"%f",self.view.frame.size.width] forKey:@"cellwidth"];
// 图片、文本高度
[dic setValue:[NSString stringWithFormat:@"%f",imgheight] forKey:@"imageheight"];
[dic setValue:[NSString stringWithFormat:@"%f",labheight] forKey:@"labheight"];
// cell总高度
[dic setValue:[NSString stringWithFormat:@"%f",labheight + imgheight] forKey:@"cellheight"];
/*
一般在cell高度在调用之前就要计算好cell的整体高度,所以同文章里所说的
什么“动态计算”,听着都是忽悠的,很多都是互相抄袭、标题党、说的玄乎罢了
*/
// 图片名字
[dic setValue:[NSString stringWithFormat:@"%d.jpg", i + 1] forKey:@"imageName"];
// 文本
[dic setValue:@"按实际佛is南方路口is女离开谁知道呢是;你温柔看;否那我可;老妇女卡洛斯的女警卡拉水泥大V看刘女士的乌克兰那我可老妇女为快乐风男考虑对方是你的快乐发是哪里看的真是女分类看是否离开 萨瓦迪卡嫁女记初中生的的女离开SZN SKNJD距中山东路东南角WE;失地农民了;你WAE法芙娜看来是在哪读离开WWNSE弗兰克SND克鲁苏NDvjklnwaKlfnawkerljnslkvcn只是看了你就开了瓦恩积分klwaNJE lknkldvnklszdnvkldzasn klandgk lnazelkr " forKey:@"info"];
// 存入数据源
[array addObject:dic];
}
return array;
}
- 很多人都在数据源这一步封装成model,交给model去处理计算宽高、数据存储,我这篇文章只是用来给萌新们解坑的,以最基本、最简单粗暴的方式来让读者懂得如何使用这些东西。
- 之前亲身体会去腾讯面试的过程,面试官在最后说的很清楚,“tableview什么动态计算都是花里胡哨的封装方法和简化步骤,并没有那么神奇”,很受用(腾讯面试基本都会问tableview的9种优化方式)。
- Tips:这里需要提醒一下大家,因为Cell上面可能有各种控件,我们需要在数据源获取的时候就完成Cell完整高度的计算,那样才能保证我们在TableView创建时候不会出现Cell错位的情况。
看到这里,大家会问具体怎么计算呢?不要着急,慢慢道来。我们一般在使用UITableview的时候绝大多数都是作为数据展示的,所以我们计算的目标对象就是:文本、图片。
3.UILabel 和 UIImageView 的高度计算
// 计算label高度
- (CGFloat)getWidth:(CGFloat)width title:(NSString *)title font:(UIFont *)font
{
// 创建一个label对象,给出目标label的宽度
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, width, 0)];
// 获得文字
label.text = title;
// 获得字体大小
label.font = font;
// 自适应
label.numberOfLines = 0;
[label sizeToFit];
// 经过自适应之后的label,已经有新的高度
CGFloat height = label.frame.size.height;
// 返回高度
return height;
}
// 计算image高度
- (CGFloat)getImagWidth:(CGFloat)width imageHeight:(CGFloat)height
{
// 获得宽高比
CGFloat scale = width / height;
// 当前tableview或者cell的宽度 除以 比例 = 高度
CGFloat newHeight = self.view.frame.size.width/* 当前宽度 */ / scale/*比例*/;
// 返回新的高度
return newHeight;
}
- 计算高度所需要的元素:
UILabel的高度是需要三个要素 | UIImageView的高度需要三个要素 |
---|---|
目标宽度 | 图片本身的大小 |
文本内容 | 图片的目标宽度 |
字体大小 | 图片宽高比列 |
这样我们就可以计算好目标高度,然后保存到数据源里面,在之后所需要的地方再取出来。
好啦,那现在我们创建好了TableView和数据源,然后该做什么呢?实现TableView的协议:
4. UITableView协议
这里呢?老套路,返回1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
这里肯定是返回咱们的数据源的个数啦(数据源结构 Array -> Dictionary)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return dataArray.count;
}
重要的就是在这里,我们把计算好的Cell高度,在这个方法里面返回
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return [[[dataArray objectAtIndex:indexPath.row]objectForKey:@"cellheight"]floatValue];
}
到了另一个重要的地方,复用池,我们需要在这里把所存的数据塞进Cell里面,怎么放置呢?我们创建自定义的Cell然后写一个get方法,在这里一一对应的获取数据
- Tips:一般不要在复用池内创建UI类的东西,会影响Tableview的性能
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * cellID = @"Cell";
TableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[TableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID];
}
if (indexPath.row < dataArray.count) {
[cell getImageName:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"imageName"] getLabelInfo:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"info"] getImageHeight:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"imageheight"] getLabelHeight:[[dataArray objectAtIndex:indexPath.row]objectForKey:@"labheight"]];
return cell;
}
return nil;
}
好啦,外部已经看到了怎么实现的,我们再来看看自定义的UITableviewCell内部怎么实现的
5. 自定义Cell
在TableViewCell.h文件里面,定义好我们需要的控件和用于接收数据的成员变量
@interface TableViewCell : UITableViewCell{
CGFloat imageHeight;
CGFloat labelHeight;
}
// 属性名
@property (nonatomic, strong) UIImageView * cellImageView;
// 属性类型
@property (nonatomic, strong) UILabel * cellLabel;
- (void)getImageName:(NSString *)name getLabelInfo:(NSString *)info getImageHeight:(NSString *)imageheight getLabelHeight:(NSString *)labelheight;
@end
让我们来TableViewCell.m文件里面实际的看看我们是如何创建的:
// 初始化方法
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.cellLabel = [[UILabel alloc]initWithFrame:CGRectZero];
self.cellImageView = [[UIImageView alloc]initWithFrame:CGRectZero];
[self.contentView addSubview:self.cellLabel];
[self.contentView addSubview:self.cellImageView];
}
return self;
}
// get方法
- (void)getImageName:(NSString *)name getLabelInfo:(NSString *)info getImageHeight:(NSString *)imageheight getLabelHeight:(NSString *)labelheight{
[self.cellImageView setImage:[UIImage imageNamed:name]];
self.cellLabel.text = info;
self.cellLabel.numberOfLines = 0;
[self.cellLabel sizeToFit];
imageHeight = [imageheight floatValue];
labelHeight = [labelheight floatValue];
}
// 视图方法
- (void)layoutSubviews{
[super layoutSubviews];
// 文字
self.cellLabel.frame = CGRectMake(0, 0, self.contentView.frame.size.width, labelHeight);
self.cellLabel.textColor = [UIColor blackColor];
self.cellLabel.font = [UIFont systemFontOfSize:15];
// 图片
self.cellImageView.frame = CGRectMake(0, labelHeight, self.contentView.frame.size.width, imageHeight);
}
Tips:在执行init方法的时候我们创建视图,并把视图添加到Cell的contentView上面去,因为Cell的self和self.contentView所承载的业务不同,我们就按照官方指引添加视图就在contentView。
Tips:需要知道的是,在这个文件里面所写的方法都是按照顺序来排列的,get方法会在layoutSubviews方法之前执行,所以我们需要在get方法里面获取数据,并填充,但是!切记不要在get方法里面执行对布局的操作,因为UITableView有一个遗留问题就是,Cell在初始化的时候,没有经过layoutSubViews这个方法之前的大小都是固定的,其详细的值是宽:320,高:44。所以我们才需要创建成员变量在get的时候获取frame、宽、高,然后在layoutSubViews方法里面对视图做布局操作。
好啦,收尾啦