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