iOS8+ UITableView自动计算cell高度并缓存

2020-11-24  本文已影响0人  文子飞_

iOS8+ UITableView自动计算cell高度并缓存

cell高度计算的历史(iOS8之前)

在iOS8之前,如果UITableViewCell的高度是动态的,如果想要显示正确的话,我们需要在下面这个UITableView的代理方法中,返回每一行的精确高度:- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

如果cell的控件很多,样式很复杂的话,在这里面我们就可能需要写很多代码去做一些复杂的计算,甚至可能导致滑动不流畅。

后来也有一些人写了一些第三方去解决这个问题,例如UITableView-FDTemplateLayoutCell。只要给cell自上而下加好约束,它就可以帮我们去算cell的高度并且可以缓存,省去了我们自己写计算代码的成本。具体可以进链接里面看看它的demo。

cell高度计算的历史(iOS8+)------ 系统的cell自适应高度的使用方法

但是在iOS10的系统下, FDTemplateLayoutCell会卡界面,而且tableview的行数越多表现的越卡。

而且苹果在iOS8之后,推出了一种超级简单的cell动态自适应的方法,使用起来比 FDTemplateLayoutCell也简单一些,而且现在iOS10都出来了,没有必要去支持iOS7了,所以最后我还是选择了用系统的办法。这样我们以后就再也不用写heightForRowAtIndexPath方法了哈哈哈。

用xib加约束和用masonry加代码约束都是可以的。注意约束一定要自上而下加好,让系统知道怎么去计算高度。加好约束后,然后告诉tableView自己去适应高度就可以了。有两种写法:

self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 100;

或者直接写这个代理方法就可以了

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 100;
}

这个的意思就是告诉tableView,你需要自己适应高度,我不给你算啦哈哈哈。但是我们需要告诉它一个大概高度,例如上面的100,理论上这个是可以随便写的,并不影响显示结果,但是越接近真实高度越好。

涉及UITableViewCell相关的布局约束,优先使用 mas_remakeConstraints 更新/更改。



开发中遇到 cell动态高度(iOS8-)

无containerViewView时
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        [self initSubViews];
        [self addConstraints];
    }
    return self;
}

#pragma mark - Private Methods
-(void)initSubViews
{
    [self.contentView addSubview:self.baseView];
    [self.contentView addSubview:self.descriptionLabel];
}

- (void)addConstraints
{   
    [self.baseView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.mas_equalTo(self);
        make.bottom.mas_equalTo(self.baseView.timeLabel);
    }];
    [self.descriptionLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.mas_equalTo(self).inset(15);
        make.top.mas_equalTo(self.baseView.mas_bottom).offset(15);
    }];
}

#pragma mark - Getters and Setters
- (WSFInformationOrderBaseView *)baseView
{
    if (!_baseView) {
        _baseView = [[WSFInformationOrderBaseView alloc] init];
    }
    return _baseView;
}

- (UILabel *)descriptionLabel
{
    if (!_descriptionLabel) {
        _descriptionLabel = [[UILabel alloc] init];
        _descriptionLabel.textColor = CTwoLevelColor;
        _descriptionLabel.font = [UIFont fontWithName:@"PingFangSC" size:13];
//        _descriptionLabel.numberOfLines = 0;
    }
    return _descriptionLabel;
}

- (void)setInformationOrderModel:(WSFInformationOrderModel *)informationOrderModel {
    if (_informationOrderModel != informationOrderModel) {
        _informationOrderModel = informationOrderModel;
    }
    self.baseView.titleLabel.text = informationOrderModel.title0;
    self.baseView.subTitleLabel.text = informationOrderModel.title1;
    self.baseView.locationLabel.titleLabel.text = informationOrderModel.title2;
    self.baseView.timeLabel.titleLabel.text = informationOrderModel.title3;
    if (informationOrderModel.orderType == WSFInformationOrderOther) {
        self.descriptionLabel.text = informationOrderModel.title4;
    } else {
        self.descriptionLabel.text = @"";
    }
    
    [self cacheCellHeightWithModel:informationOrderModel];
}

- (void)cacheCellHeightWithModel:(WSFInformationOrderModel *)model {

    CGRect cellFrame = self.frame;
    cellFrame.size.width = [UIScreen mainScreen].bounds.size.width;
    self.frame = cellFrame;

    [self layoutIfNeeded];
    [self setNeedsLayout];
    
    CGFloat cellHeight = 0.0;
    if (model.orderType == WSFInformationOrderOther) {
        cellHeight = CGRectGetMaxY(self.descriptionLabel.frame);
    } else {
        cellHeight = CGRectGetMaxY(self.baseView.frame);
    }
    model.cellHeight = cellHeight;
    
}
有containerViewView时
@interface WSFUninterestedInformationOrderCell ()
@property (nonatomic, strong) UIView *containerView;
@property (nonatomic, strong) WSFInformationOrderBaseView *baseView;
@property (nonatomic, strong) UILabel *descriptionLabel;
@end

@implementation WSFUninterestedInformationOrderCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        [self initSubViews];
        [self addConstraints];
    }
    return self;
}

#pragma mark - Private Methods
-(void)initSubViews
{
    [self.contentView addSubview:self.containerView];
    [self.containerView addSubview:self.baseView];
    [self.containerView addSubview:self.descriptionLabel];
}

- (void)addConstraints
{
    [self.containerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.mas_equalTo(self.contentView).inset(10);
        make.bottom.mas_equalTo(self.contentView);
    }];
    
    [self.baseView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.top.mas_equalTo(self.containerView);
        make.bottom.mas_equalTo(self.baseView.timeLabel);
    }];
    [self.descriptionLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.right.mas_equalTo(self.containerView).inset(15);
        make.top.mas_equalTo(self.baseView.mas_bottom).offset(15);
    }];
}

#pragma mark - Getters and Setters
- (UIView *)containerView {
    if (!_containerView) {
        _containerView = [[UIView alloc] init];
        _containerView.backgroundColor = [UIColor whiteColor];
        _containerView.layer.cornerRadius = 8.0;
    }
    return _containerView;
}

- (WSFInformationOrderBaseView *)baseView
{
    if (!_baseView) {
        _baseView = [[WSFInformationOrderBaseView alloc] init];
    }
    return _baseView;
}

- (UILabel *)descriptionLabel
{
    if (!_descriptionLabel) {
        _descriptionLabel = [[UILabel alloc] init];
        _descriptionLabel.textColor = CTwoLevelColor;
        _descriptionLabel.font = [UIFont fontWithName:@"PingFangSC" size:13];
        _descriptionLabel.numberOfLines = 0;
    }
    return _descriptionLabel;
}

- (void)setInformationOrderModel:(WSFInformationOrderModel *)informationOrderModel {
    if (_informationOrderModel != informationOrderModel) {
        _informationOrderModel = informationOrderModel;
    }
    self.baseView.titleLabel.text = informationOrderModel.title0;
    self.baseView.subTitleLabel.text = informationOrderModel.title1;
    self.baseView.locationLabel.titleLabel.text = informationOrderModel.title2;
    self.baseView.timeLabel.titleLabel.text = informationOrderModel.title3;
    if (informationOrderModel.orderType == WSFInformationOrderOther) {
        self.descriptionLabel.text = informationOrderModel.title4;
    } else {
        self.descriptionLabel.text = @"";
    }
    
    [self cacheCellHeightWithModel:informationOrderModel];
}

- (void)cacheCellHeightWithModel:(WSFInformationOrderModel *)model {
    
    CGRect cellFrame = self.frame;
    cellFrame.size.width = [UIScreen mainScreen].bounds.size.width;
    self.frame = cellFrame;
    
    [self layoutIfNeeded];
    [self setNeedsLayout];
    
    CGFloat cellHeight = 0.0;
    if (model.orderType == WSFInformationOrderOther) {
        cellHeight = CGRectGetMaxY(self.descriptionLabel.frame) + 15;
    } else {
        cellHeight = CGRectGetMaxY(self.baseView.frame) + 15;
    }
    
    model.cellHeight = cellHeight + 15;
    
}

@end

ViewController.m

#pragma mark - <UITableViewDataSource>
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return self.commetsArrays.count;
    
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    NSLog(@"cellForRowAtIndexPath_Comment");
    
    BDCommentTableCell *commentCell = [tableView dequeueReusableCellWithIdentifier:Cell_ID forIndexPath:indexPath];
    BDCommentViewModel *model = self.commetsArrays[indexPath.row];
    commentCell.commentModel = model;
    
    //[commentCell setNeedsUpdateConstraints];
    //[commentCell updateConstraintsIfNeeded];
    
    return commentCell;
    
}


#pragma mark <UITableViewDelegate>
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    BDCommentViewModel *model = self.commetsArrays[indexPath.row];
    NSLog(@"name = %@---height = %.2f---isReply = %d--->>", model.nameStr, model.cellHeight, model.isReply);
    return model.cellHeight;
    
}


#pragma mark - getter/setter
- (void)setCommetsArrays:(NSArray *)commetsArrays {
    if (_commetsArrays != commetsArrays) {
        _commetsArrays = commetsArrays;
        [self reloadData];
    }
}

BDCommentTableCell.m

#pragma mark --- Private Functions ---
- (void)addConstraints {

    // _headView给个原始高度,后面修改高度。以免出问题
    _headView=[WSFeedBackCellBaseView new];
    [_headView showBlueLayer];
    [self.contentView addSubview:_headView];
    [_headView mas_makeConstraints:^(MASConstraintMaker *make) {
          make.top.left.right.mas_equalTo(self.contentView);
          make.height.mas_equalTo(150);
    }];    

}

#pragma mark - getter/setter
- (void)setCommentModel:(BDCommentViewModel *)commentModel {
    
    if (_commentModel != commentModel) {
        _commentModel = commentModel;
    }
    
    self.nameLabel.text = commentModel.nameStr;
    self.contentLabel.text = commentModel.commentStr;
    self.timeLabel.text = commentModel.timeStr;
    
    if (commentModel.isReply) {
        
        /** 《重点》
         *  Label的text      影响Label高度
         *  Label的hidden    影响Label显示隐藏
         */
        BDReplyModel *replyModel = commentModel.replyModel;
        self.replyNameLabel.text = replyModel.nameStr;
        self.replyContentLabel.text = replyModel.replyStr;
        
        self.replyNameLabel.hidden = NO;
        self.replyContentLabel.hidden = NO;
        
    } else {
        
        /** 《重点》
         *  Label的text      影响Label高度
         *  Label的hidden    影响Label显示隐藏
         */
        self.replyNameLabel.text = nil;
        self.replyContentLabel.text = nil;
        self.replyNameLabel.hidden = YES;
        self.replyContentLabel.hidden = YES;
        
    }
    
    /**
     *  《重点》返回单元格高度
     *  **强制布局之前,需要先手动设置下cell的真实宽度,以便于准确计算**
     */
    CGRect cellFrame = self.frame;
    cellFrame.size.width = [UIScreen mainScreen].bounds.size.width;
    self.frame = cellFrame;

    [self.headView layoutIfNeeded];
    
    // 更新headView 高度
    CGFloat headViewHeight = CGRectGetMaxY(self.headView.timeOrderLab.frame);
    [self.headView mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.left.right.mas_equalTo(self.contentView);
        make.height.mas_equalTo(headViewHeight);
    }];

    if (commentModel.cellHeight <= 0) {
        CGFloat Cell_Height = CGRectGetMaxY(self.timeLabel.frame);
        commentModel.cellHeight = Cell_Height;
    }
    
    [self setNeedsLayout];
    
}

#pragma mark - <缓存cell高度>
- (void)cacheCellHeightWithModel:(WSFeedBackCashListDataListModel *)model {
    
    // 强制布局之前,需要先手动设置下cell的真实宽度,以便于准确计算
    CGRect cellFrame = self.frame;
    cellFrame.size.width = [UIScreen mainScreen].bounds.size.width;
    self.frame = cellFrame;
    
    // 更新headView 高度
    [self.headView layoutIfNeeded];
    CGFloat headViewHeight = CGRectGetMaxY(self.headView.timeOrderLab.frame);
    [self.headView mas_updateConstraints:^(MASConstraintMaker *make) {
        make.height.mas_equalTo(headViewHeight);
    }];
    
    // 一定要强制布局下,否则拿到的高度不准确
    [self layoutIfNeeded];
    CGFloat cellHeight = CGRectGetMaxY(self.btnComplain.frame) + 20;
    model.cellHeight = cellHeight;
    
    [self setNeedsLayout];
    
}

ViewController.m

// 控制器里 要给Cell一个预估高度。使得
// - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 先执行,布局Cell和子视图,并计算cellHeight
// - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 后执行,获取cellHeight

#pragma mark - <给Cell一个预估高度>
self.tableView.estimatedRowHeight = 160.0f;
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 160.0f;
}

#pragma mark - <预估高度,还是获取不到高度。用以下方式创建单元格>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"WSFRefundBaseCell"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"WSFRefundBaseCell"];
    }
    RefundModel *refundModel = _refundModels[indexPath.row];
    cell.refundModel = refundModel;
    return cell;
}





开发中遇到 cell动态高度(iOS8+)

ViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    if (@available(iOS 8.0, *)) {
        self.tableView.rowHeight = UITableViewAutomaticDimension;
        self.tableView.estimatedRowHeight = 100;
    } else {
        
    }
}

#pragma mark --- UITableViewDelegate ---
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath API_AVAILABLE(ios(7.0)) {
    return 100;
}

CommentRecordCell.m

#pragma mark --- LifeCycle ---
- (void)prepareForReuse {
    [super prepareForReuse];
    
    [self.commentTagView.tagsArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [self.commentTagView removeTag:obj];
    }];
    
}

#pragma mark --- setter ---
- (void)setCommentModel:(WSFCommentModel *)commentModel { 
      
      // ....此处省略N行代码
      if (commentModel.rateLabel.count > 0) {
        //self.self.commentTagView.hidden = false;
        [self.orderView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.right.equalTo(self.containerView).insets(UIEdgeInsetsMake(0, 15, 0, 15));
            make.top.mas_equalTo(self.commentTagView.mas_bottom).offset(8);
        }];
    } else {
        //self.self.commentTagView.hidden = true;
        //当使用mas_updateConstraints更新约束不起作用,或者有问题。建议改为mas_remakeConstraints
        [self.orderView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.right.equalTo(self.containerView).insets(UIEdgeInsetsMake(0, 15, 0, 15));
            make.top.mas_equalTo(self.commentContentLabel.mas_bottom).offset(7);
        }];
    }
    
    [self layoutIfNeeded];

}



效果图如下:

image.png
上一篇下一篇

猜你喜欢

热点阅读