继上篇文章说起cell高度的自适应起,已经好久了,上一篇是介绍了如何使用FDTemplateLayoutCell这个第三方库来解决。但是这毕竟是别人的东西,好用是好用,在遇到某些需求的时候还是不如自己写的好控制。
项目新版本也告一段落了,在项目中也正好有这种需求,刚开始我也是打算直接用上面提到库解决的,但是后来发现行不通,于是在查阅资料以及咨询大神后,得出一套适用于绝大多数场景的方案。
以我们公司项目为例,是一个社区的时间线的需求,类似QQ空间那种,很常见也是最经典的cell高度自适应需求的例子。
大神请轻喷(其实我也挺慌的),只为和我一样的菜鸟少走弯路,欢迎提出意见(讲真,我觉得这种方法很2b,太麻烦了)。
进入正题
先看效果图
上代码
其实实现最重要的就一个类,负责根据数据源计算cell中各个控件的frame的类
SmartCellFrame.h
#import <Foundation/Foundation.h>
#import "SmartModel.h"
@interface SmartCellFrame : NSObject
@property (nonatomic,assign) CGRect nameFrame;
@property (nonatomic,assign) CGRect dateFrame;
@property (nonatomic,assign) CGRect headShowFrame;
@property (nonatomic,assign) CGRect contentFrame;
@property (nonatomic,assign) CGRect addressFrame;
@property (nonatomic,assign) CGRect imagesFrame;
@property (nonatomic,strong) SmartModel *model; //数据源
@property (nonatomic,assign) CGFloat imageSize; //每张图片的size
@property (nonatomic,assign) CGFloat cellHeight; //cell的高度
@end
定义了各个控件的frame,简单起见,我只定义了6个,但是不管多少个原理都是一样的。
SmartCellFrame.m
#import "SmartCellFrame.h"
@implementation SmartCellFrame
-(void)setModel:(SmartModel *)model{
_model = model;
CGFloat margin = 15; //边距
CGFloat screenWith = [UIScreen mainScreen].bounds.size.width;
//依次计算各个控件frame;
//头像的frame
CGFloat headx = margin;
CGFloat heady = margin;
CGFloat headw = 46;
CGFloat headh = 46;
_headShowFrame = CGRectMake(headx, heady, headw, headh);
CGFloat namex = headx+headw+10;
CGFloat namey = heady+5;
CGFloat namew = screenWith-namex-margin;
CGFloat nameh = 20;
_nameFrame = CGRectMake(namex, namey, namew, nameh);
CGFloat datex = namex;
CGFloat datey = heady+headh-20;
CGFloat datew = 200;
CGFloat dateh = 20;
_dateFrame = CGRectMake(datex, datey, datew, dateh);
CGFloat contentx = margin;
CGFloat contenty = heady+headh+margin;
CGFloat contentw = screenWith-2*margin;
CGFloat contenth = 0;
CGSize contentSize = [model.content boundingRectWithSize:CGSizeMake(contentw, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont systemFontOfSize:16],NSFontAttributeName,nil] context:nil].size;
contenth = contentSize.height;
_contentFrame = CGRectMake(contentx, contenty, contentw, contenth);
CGFloat imageListx = contentx;
CGFloat imageListy = contenty+contenth+margin;
CGFloat imageListw = (screenWith - margin-10-15);
CGFloat imageListh = 0.0;
NSInteger count = [model.images count];
_imageSize = imageListw/3;
CGFloat imageMargin = 2;
if (count>9) {
count =9;
}
if (count==0) {
imageListh = 0.0;
}
if (count==1) {
CGFloat maxH = _imageSize*2;
Image *image = model.images[0];
if (image.height>=image.width) {
//高度大于宽,以高度为准
if(image.height<=maxH){
imageListh = image.height;
imageListw = image.width;
}
if (image.height>maxH) {
imageListh = maxH;
imageListw = image.width*(imageListh/image.height);
}
}
if (image.height<image.width) {
if (image.width<=maxH) {
imageListh = image.height;
imageListw = image.width;
}
if (image.width>maxH) {
imageListw = maxH;
imageListh = image.height*(imageListw/image.width);
}
}
}
if (count>1&&count<=3) {
imageListh = _imageSize+imageMargin;
}
if (count>3&&count<=6) {
imageListh = imageMargin +_imageSize*2;
}
if (count>6&&count<=9) {
imageListh = imageMargin*2 +_imageSize*3;
}
_imagesFrame = CGRectMake(imageListx, imageListy, imageListw, imageListh);
CGFloat addressx = margin;
CGFloat addressy = imageListy+imageListh+margin;
CGFloat addressw = 200;
CGFloat addressh = 20;
_addressFrame = CGRectMake(addressx, addressy, addressw, addressh);
//计算cell高度
_cellHeight =addressy+addressh+margin;
}
@end
这里计算frame的时候很多我是直接写死的大小,能写死的就写死吧,有变动需求的可以根据服务端返回的数据来变动,可参考上面content文本计算高度的方式,另外,图片的大小,一般来说最多就是九宫格,一张图片时做特殊处理,这个时候服务端最好能返回图片的尺寸。
最重要的类就是上面的了,其实也不难。
SmartHeightCellViewController.m 控制器类
#import "SmartHeightCellViewController.h"
#import "SmartModel.h"
#import "SmartTableViewCell.h"
@interface SmartHeightCellViewController ()<UITableViewDataSource,UITableViewDelegate>
@property (nonatomic,strong) UITableView *table;
@property (nonatomic,strong) NSMutableArray<SmartCellFrame *> *tableDatas;
@end
@implementation SmartHeightCellViewController
-(void)viewDidLoad{
self.view.backgroundColor = [UIColor colorWithRed:0.980 green:0.980 blue:0.980 alpha:1.00];
self.title = @"cell高度自适应";
[self configdatas];
[self configTable];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [_tableDatas count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
SmartTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"smart"];
if (cell==nil) {
cell = [[SmartTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"smart"];
}
//给cell设置model,cell的代码见下面
[cell setCellFrame:_tableDatas[indexPath.row]];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
//这里就是给cell设置高度的地方,直接取我们算好的高度
return _tableDatas[indexPath.row].cellHeight;
}
//最主要就是这个方法了
-(void)configdatas{
_tableDatas = [[NSMutableArray alloc] init];
NSArray *models = [SmartModel models]; //这个是我自己写的生成model的方法,可无视
//这里将model转化为上面提到的计算的frame的类,这个类里就包含了cell里所有的布局信息以及cell的高度
for (SmartModel *model in models) {
SmartCellFrame *frame = [[SmartCellFrame alloc] init];
[frame setModel:model];
[_tableDatas addObject:frame];
}
}
-(void)configTable{
//初始化UITable,我是用的代码写的(原谅本人不会用sb)
_table = [[UITableView alloc] init];
_table.delegate = self;
_table.dataSource = self;
_table.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_table];
[_table mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.view);
}];
}
@end
上面注释相信已经写的很详细了,就一个UITable,我不信有人看不懂或者懒得看的(这特么才几行代码你J就懒得看?),下面就是cell的代码
SmartTableViewCell.h
#import <UIKit/UIKit.h>
#import "SmartCellFrame.h"
@interface SmartTableViewCell : UITableViewCell
@property (nonatomic,strong) UIImageView *head;
@property (nonatomic,strong) UILabel *name;
@property (nonatomic,strong) UILabel *date;
@property (nonatomic,strong) UILabel *content;
@property (nonatomic,strong) UIView *images;
@property (nonatomic,strong) UILabel *address;
@property (nonatomic,strong) SmartCellFrame *cellFrame;//计算frame的类,其实也含有数据源model
@end
SmartTableViewCell.m
#import "SmartTableViewCell.h"
@interface SmartTableViewCell ()
@property (nonatomic,strong) NSMutableArray *imageViews;
@property (nonatomic,strong) NSArray *imagesArr;
@end
@implementation SmartTableViewCell
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.contentView.backgroundColor = [UIColor whiteColor];
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self setUp];
}
return self;
}
-(void)setUp{
_head = [[UIImageView alloc] init];
_head.layer.masksToBounds = YES;
_head.layer.cornerRadius = 23;
_head.contentMode = UIViewContentModeScaleAspectFill;
[self.contentView addSubview:_head];
_name = [[UILabel alloc] init];
_name.font = [UIFont systemFontOfSize:16];
_name.textColor = [UIColor colorWithRed:1.000 green:0.251 blue:0.506 alpha:1.00];
[self.contentView addSubview:_name];
_date = [[UILabel alloc] init];
_date.font = [UIFont systemFontOfSize:12];
_date.textColor = [UIColor colorWithRed:1.000 green:0.502 blue:0.671 alpha:1.00];
[self.contentView addSubview:_date];
_content = [[UILabel alloc] init];
_content.numberOfLines = 0;
_content.font = [UIFont systemFontOfSize:16];
_content.textColor = [UIColor colorWithRed:0.129 green:0.129 blue:0.129 alpha:1.00];
[self.contentView addSubview:_content];
_address = [[UILabel alloc] init];
_address.font = [UIFont systemFontOfSize:12];
_address.textColor = [UIColor colorWithRed:0.459 green:0.459 blue:0.459 alpha:1.00];
[self.contentView addSubview:_address];
_images = [[UIView alloc] init];
_images.backgroundColor = [UIColor whiteColor];
[self.contentView addSubview:_images];
_imageViews = [[NSMutableArray alloc] init];
for (int i=0; i<9; i++) {
UIImageView *imageview = [[UIImageView alloc] init];
imageview.backgroundColor = [UIColor colorWithRed:0.933 green:0.933 blue:0.933 alpha:1.00];
[_imageViews addObject:imageview];
}
}
// 最主要的就是这个类了
-(void)setCellFrame:(SmartCellFrame *)cellFrame{
_cellFrame = cellFrame;
_head.frame = cellFrame.headShowFrame;
_name.frame = cellFrame.nameFrame;
_date.frame = cellFrame.dateFrame;
_content.frame = cellFrame.contentFrame;
_images.frame = cellFrame.imagesFrame;
_address.frame = cellFrame.addressFrame;
_head.image = [UIImage imageNamed:cellFrame.model.headShowURL];
_name.text = cellFrame.model.name;
_date.text = cellFrame.model.date;
_content.text = cellFrame.model.content;
_address.text = cellFrame.model.address;
_imagesArr = cellFrame.model.images;
[self setUpImageViews];
}
//这个是布局图片的,挺简单的,我就不多说了
-(void)setUpImageViews{
for (UIImageView *view in _images.subviews) {
[view removeFromSuperview];
}
NSInteger count = [_imagesArr count];
if (count==0) {
return;
}
if (count==1) {
UIImageView *imageView = _imageViews[0];
Image *image = _imagesArr[0];
imageView.layer.masksToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.image = [UIImage imageNamed:image.imageURL];
imageView.frame = CGRectMake(0, 0, CGRectGetWidth( _cellFrame.imagesFrame),CGRectGetHeight( _cellFrame.imagesFrame));
[_images addSubview:imageView];
}
if (count>1) {
int i=0;
for (Image *image in _imagesArr) {
if (i==9) {
break;
}
UIImageView *imageView = _imageViews[i];
imageView.frame = CGRectMake(i%3*_cellFrame.imageSize, i/3*_cellFrame.imageSize, _cellFrame.imageSize-2, _cellFrame.imageSize-2);
imageView.layer.masksToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFill;
[_images addSubview:imageView];
imageView.image = [UIImage imageNamed:image.imageURL];
i++;
}
}
}
@end
还有个类没贴出来,其实就是个model类,我自己编的数据,不看也罢。
这种方法其实是最简单无脑的,根据数据源和布局方式计算高度,简单粗暴,而且好控制,适合新手,就是代码写得多,布局简单点的还好,复杂点的估计会崩溃😂