美文网首页
iOS8+ UITableView自动计算cell高度并缓存

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

作者: 文子飞_ | 来源:发表于2020-11-24 16:35 被阅读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

    相关文章

      网友评论

          本文标题:iOS8+ UITableView自动计算cell高度并缓存

          本文链接:https://www.haomeiwen.com/subject/staiiktx.html