总体思路:
- 保存一个设置好约束的指定Cell类的对象
- 给Cell类对象填充需要显示的所有数据项(UILabel值、UIImageView图片...)
- 然后在返回cell高度的回调函数中
- 使用 [cell对象.cententView systemLayoutSizeFittingSize:UILayoutFitttingCompressSize] 让ios sdk替我们计算cell的真实高度。
第一步、定义所有cell必须实现的对象方法--》将传入的实体对象设置给cell.contentView.subViews
#import <Foundation/Foundation.h>
@protocol ZSYAutoLayoutCellProtocol;
static CGFloat ZSYDynamicTableViewCellAccessoryWidth = 33;
//这个数组用来保存所有类型的自动布局的Cell类的一个对象,用于后面计算cell高度
static NSMutableArray *cellArray;
//先传出保存的cell对象,然后再用调用者返回设置了所有显示数据的cell对象,用于计算cell高度需要
typedef id (^setupCellBlock)(id<ZSYAutoLayoutCellProtocol> cellToSetup);
@protocol ZSYAutoLayoutCellProtocol <NSObject>
//所有cell设置数据
- (void)setupDataItem:(id)data;
//计算cell高度
+ (CGSize)sizeForCellWithDefaultSize:(CGSize)defaultSize setupCellBlock:(setupCellBlock)block;
@end
第二步、写一个BaseCell做一个默认实现接口
- (void)setupDataItem:(id)data {
//这个方法子类cell重写,将实体对象设置给cell.contentView.subviews
}
+ (CGSize)sizeForCellWithDefaultSize:(CGSize)defaultSize setupCellBlock:(setupCellBlock)block {
__block UITableViewCell *cell = nil;
//查看静态数组内是否已经存在当前Cell类的一个对象
[cellArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[[self class] class]]) {
cell = obj;
*stop = YES;
}
}];
//如果不存在,创建一个当前Cell类型的对象,并保存到数组。后续计算当前Cell类型对象的高度时,直接使用这个保存的Cell对象
if (!cell) {
cell = [[[self class] alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"XZHAutoLayoutCellIdentifier"];
cell.frame = CGRectMake(0, 0, defaultSize.width, defaultSize.height);
[cellArray addObject:cell];
}
//获取到设置了属性值的cell对象[1.先回传出去 2.再接收Block的一个返回值cell对象]
cell = block((id<ZSYAutoLayoutCellProtocol>) cell);
//获取到cell对象的 CGSize
// [cell.contentView layoutIfNeeded];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
size.width = MAX(defaultSize.width, size.width);
size.height = size.height + 1.0f;
return size;
}
第三步、编写某个具体的Cell
// 实现接口定义的设置实体类对象的方法
- (void)setupDataItem:(id)data {
if ([data isKindOfClass:[ZSYEntityModule class]]) {
_entity = (ZSYEntityModule *)data;
_titleLabel.text = _entity.title;
[_contentImageView sd_setImageWithURL:[NSURL URLWithString:_entity.imageURL]
placeholderImage:[UIImage imageNamed:@"blankpage_image_Hi"]
options:SDWebImageProgressiveDownload];
//UILabel写上这一句
[_titleLabel layoutIfNeeded];
}
}
//给Cell.contentView.subviews分别设置约束,使用VFL
- (void)initSubviews {
self.backgroundColor = [UIColor whiteColor];
self.accessoryType = UITableViewCellAccessoryNone;
self.accessoryView = nil;
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self.contentView setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
_titleLeftImageView = [[UIImageView alloc] init];
_titleLeftImageView.image = [UIImage imageNamed:@"icon_title"];
_titleLeftImageView.translatesAutoresizingMaskIntoConstraints = NO;
_titleLeftImageView.contentMode = UIViewContentModeScaleAspectFill;
_titleLabel = [[UILabel alloc] init];
_titleLabel.numberOfLines = 0;
_titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
_titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
_titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:15];
_titleLabel.textColor = [UIColor hex:@"#514647"];
_contentImageView = [[UIImageView alloc] init];
_contentImageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:_titleLeftImageView];
[self.contentView addSubview:_titleLabel];
[self.contentView addSubview:_contentImageView];
NSDictionary *viewDict = NSDictionaryOfVariableBindings(_titleLeftImageView, _titleLabel, _contentImageView);
NSDictionary *metricDict = @{@"sideBuffer1" : @16,
@"sideBuffer2" : @10,
@"vertical_top_Buffer" : @12,
@"vertical_middle_Buffer" : @16,
@"vertical_bottom_Buffer" : @12.5,
@"labelImageSizeWith" : @(_titleLeftImageView.image.size.width),
@"labelImageSizeHeight" : @(_titleLeftImageView.image.size.height),
@"contentImageSizeHeight" : @181};
NSString *vfl = @"H:|-sideBuffer1-[_titleLeftImageView(labelImageSizeWith)]-5-[_titleLabel]-sideBuffer2-|";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl options:0 metrics:metricDict views:viewDict]];
vfl = @"H:|-sideBuffer2-[_contentImageView]-sideBuffer2-|";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl options:0 metrics:metricDict views:viewDict]];
vfl = @"V:|-vertical_top_Buffer-[_titleLeftImageView]";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl options:0 metrics:metricDict views:viewDict]];
vfl = @"V:|-vertical_top_Buffer-[_titleLabel]-vertical_middle_Buffer-[_contentImageView(contentImageSizeHeight)]-vertical_bottom_Buffer-|";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl options:0 metrics:metricDict views:viewDict]];
// vfl = @"V:[_contentImageView(contentImageSizeHeight)]-vertical_bottom_Buffer-|";
// [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:vfl options:0 metrics:metricDict views:viewDict]];
// [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.titleLeftImageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.titleLabel attribute:NSLayoutAttributeTop multiplier:1 constant:0]];
[self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.titleLeftImageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.titleLabel attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
// [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:self.titleLeftImageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.titleLabel attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
[self.titleLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.titleLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
//获取每个cell重写后,自己的默认size
CGSize defaultSize = [[self class] defaultCellSize];
//注意: 所有UILabel如果需要高度动态改变,一定要设置preferredMaxLayoutWidth最大宽度
self.titleLabel.preferredMaxLayoutWidth = defaultSize.width - [metricDict[@"sideBuffer1"] floatValue] -[metricDict[@"labelImageSizeWith"] integerValue] - 5 - [metricDict[@"sideBuffer2"] integerValue];
}
第四步、tableview回调代理函数cell高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (![[self cellClass] isDynamic]) {
return [self cellDefaultHeight];
}
__weak typeof(self) weakSelf = self;
//默认 size
CGSize defaultSize = [[self cellClass] defaultCellSize];
//计算后自适应的 size
CGSize cellSize = [[self cellClass] sizeForCellWithDefaultSize:defaultSize
setupCellBlock:^id(id<ZSYAutoLayoutCellProtocol> cellToSetup)
{
NSArray *dataList = [weakSelf dataSource];
if ([self sectionCount] == 1) {//一维数组
[cellToSetup setupDataItem:dataList[[indexPath row]]];
} else {//二唯数组
NSArray *sectionArray = dataList[indexPath.section];
id obj = sectionArray[indexPath.row];
[cellToSetup setupDataItem:obj];
}
return cellToSetup;
}];
return cellSize.height;
}