Cell的动态高度有很多种方法。在这里我主要记录一下自适应自iOS7开始支持的estimatedRowHeight
自适应方法。
这里我以一个简陋的“朋友圈”为例子。会有一个动态高度的文本,和一个动态数量的九宫格图片。
用到的第三方框架是Masonry
。
1、初始化tableView
在ViewController
里,初始化我们的tableView
,在这里tableView
是ViewController
的属性变量。
_tableView = [UITableView new];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.allowsSelection = NO;
[_tableView registerClass:[SmartTableViewCell class] forCellReuseIdentifier:kCellIdentifier];
[self.view addSubview:_tableView];
[_tableView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.view).with.offset(20);
make.left.equalTo(self.view);
make.bottom.equalTo(self.view);
make.width.equalTo(self.view);
}];
_tableView.estimatedRowHeight = 60;
重要的是最后一句代码。estimatedRowHeight
为预估行高,其实随便设置个值都成反正我们是自适应的,但是不能不设。
至于很多教程里说的_tableView.rowHeight = UITableViewAutomaticDimension;
,其实我发现不写也没关系。查看API也可以发现UITableViewAutomaticDimension
// Returning this value from tableView:heightForHeaderInSection: or tableView:heightForFooterInSection: results in a height that fits the value returned from
// tableView:titleForHeaderInSection: or tableView:titleForFooterInSection: if the title is not nil.
在tableView:heightForHeaderInSection
或者tableView:heightForFooterInSection:
的返回值是适应高度。是针对Header和Footer的,和Cell本身没啥关系。
2、初始化UItableViewCell
首先把控件摆出来。有一个名字,一个消息,一个九宫格。
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_nameLabel = [UILabel new];
[_nameLabel setTextColor:[UIColor blueColor]];
[self.contentView addSubview:_nameLabel];
[_nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(self.contentView);
make.left.equalTo(self.contentView);
make.right.equalTo(self.contentView);
}];
_messageLabel = [[UILabel alloc]init];
_messageLabel.numberOfLines = 0;
[self.contentView addSubview:_messageLabel];
[_messageLabel mas_makeConstraints:^(MASConstraintMaker *make){
make.left.equalTo(self.contentView);
make.right.equalTo(self.contentView);
make.top.equalTo(_nameLabel.mas_bottom);
make.bottom.equalTo(self.contentView).priorityLow();
}];
_imageViews = [NSMutableArray new];
for (int i=0; i<kMaxImageCount; i++) {
UIImageView *imageView = [UIImageView new];
[self.contentView addSubview:imageView];
[_imageViews addObject:imageView];
}
}
return self;
}
Cell
的高度需要自适应,方法是约束contentView
的top
和bottom
。
nameLabel
中我指定make.top.equalTo(self.contentView);
messageLabel
中我指定了make.bottom.equalTo(self.contentView).priorityLow();
这样一来,不管我的nameLabel
和messageLabel
有几排,甚至中间还有没有别的控件,反正Cell
的top
和bottom
总是将nameLabel
和messageLabel
包含在内的。如此就实现了在没有九宫格时的高度自适应。
在这里我并没有九宫格的的约束,只是将控件摆了上去。因为在九宫格是动态的,我在初始化控件时并不知道Cell
的bottom
应该与哪个Image
相约束。
3、UITableViewCell的赋值
在这里需要注意的是,由于Cell
的高度是自适应的,所以我们不能向寻常Cell
赋值一样,将Model
传入Cell
后,在-(void)layoutSubviews;
里进行赋值。应该在赋值时,直接把值给控件,然后让控件去自适应。
这里我自定义了一个简单的Model
:
@interface Info : NSObject
@property(nonatomic, strong)NSString *name, *message;
@property(nonatomic, strong)NSMutableArray *images;
@end
@implementation Info
@end
赋值时:
- (void)setInfo:(Info *)info {
_info = info;
self.nameLabel.text = _info.name;
self.messageLabel.text = _info.message;
NSInteger count = [[_info images]count];
for (int i=0; i<kMaxImageCount; i++) {
[_imageViews[i] setHidden:YES];
[_imageViews[i] mas_remakeConstraints:^(MASConstraintMaker *make){
make.width.mas_equalTo(IMAGE_WIDTH);
make.height.mas_equalTo(IMAGE_HEIGHT);
make.centerX.equalTo(self.contentView).multipliedBy(((i%3)*2+1.0)/3);
make.top.equalTo(_messageLabel.mas_bottom).offset(IMAGE_HEIGHT*(i/3));
}];
}
if (count>0) {
for (int i=0; i<count; i++) {
[_imageViews[i] setImage:[_info images][i]];
[_imageViews[i] setHidden:NO];
if (i==count-1) {
[_imageViews[i] mas_makeConstraints:^(MASConstraintMaker *make){
make.bottom.equalTo(self.contentView).priorityHigh();
}];
}
}
}
}
由于没有找到Masonry
删除某条约束的方法,所以在这里每次赋值时,干脆重新还原了所有image
的约束,这样可以确保Cell
在复用的时候,不会受到影响。(这块可以优化)
接下来,遍历用户的图片并依次设置到九宫格,当遍历到最后一张图片,则设置当前UIImageView
的bottom
就是Cell
的bottom
。
注意一个细节,我设置了两遍Cell
的bottom
,分别是messageLabel
和九宫格的最后一个imageView
。
[_messageLabel mas_makeConstraints:^(MASConstraintMaker *make){
make.left.equalTo(self.contentView);
make.right.equalTo(self.contentView);
make.top.equalTo(_nameLabel.mas_bottom);
make.bottom.equalTo(self.contentView).priorityLow();
}];
[_imageViews[i] mas_makeConstraints:^(MASConstraintMaker *make){
make.bottom.equalTo(self.contentView).priorityHigh();
}];
他们并不会相互冲突,Masonry
默认的优先级为priorityMedium()
,约束时会按照优先级优先进行约束。在没有图片时,只有messageLabel
对Cell
的bottom
进行了约束,于是Cell
的bottom
就是messageLabel
的bottom
;有图片时,最后一张图片也对Cell
的bottom
进行了约束,且优先级更高,于是Cell
的bottom
约束更改为最后一张图片的bottom
。
如此,全部完成。点击下载Demo