UITableViewCell自適應高度完整示例

Cell的動態高度有很多種方法。在這里我主要記錄一下自適應自iOS7開始支持的estimatedRowHeight自適應方法。


這里我以一個簡陋的“朋友圈”為例子。會有一個動態高度的文本,和一個動態數量的九宮格圖片。
用到的第三方框架是Masonry

1、初始化tableView

ViewController里,初始化我們的tableView,在這里tableViewViewController的屬性變量。

_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的高度需要自適應,方法是約束contentViewtopbottom
nameLabel中我指定make.top.equalTo(self.contentView);
messageLabel中我指定了make.bottom.equalTo(self.contentView).priorityLow();

這樣一來,不管我的nameLabelmessageLabel有幾排,甚至中間還有沒有別的控件,反正Celltopbottom總是將nameLabelmessageLabel包含在內的。如此就實現了在沒有九宮格時的高度自適應。

在這里我并沒有九宮格的的約束,只是將控件擺了上去。因為在九宮格是動態的,我在初始化控件時并不知道Cellbottom應該與哪個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在復用的時候,不會受到影響。(這塊可以優化)
接下來,遍歷用戶的圖片并依次設置到九宮格,當遍歷到最后一張圖片,則設置當前UIImageViewbottom就是Cellbottom

注意一個細節,我設置了兩遍Cellbottom,分別是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(),約束時會按照優先級優先進行約束。在沒有圖片時,只有messageLabelCellbottom進行了約束,于是Cellbottom就是messageLabelbottom;有圖片時,最后一張圖片也對Cellbottom進行了約束,且優先級更高,于是Cellbottom約束更改為最后一張圖片的bottom

如此,全部完成。點擊下載Demo

gif.gif
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容