TableView的Cell高度计算那些坑

在iOS中使用最多的就是Tableview这个控件了。关于Cell的高度计算,很多很多的文章都有说。此文赘述的是我不常用的Cell计算方法,但是还是很实用的。

现在的我,最常用的是Autolayout的Cell自动计算方式,创建定义的TableViewCell都会用xib文件的方式。只要合理的设置好Cell内部控件的约束关系,Cell的高度就不用我们手动去计算了,What’s More!,连TableView的高度计算HeightForRow的代理函数都不用写,非常简单至极。

这里说的是手动的计算,需要重写Tableview的高度计算代理函数。使用到一个重要的系统函数:

- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize NS_AVAILABLE_IOS(6_0);

首先,创建Cell,并使用代码的方式,用Masonry添加相关的约束。这个约束关系需要合理,可以通过约束自动计算出Cell高度的方式。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (!_templateCell) {
        _templateCell = [tableView dequeueReusableCellWithIdentifier:@"CELL_IDENTIFIER"];
    }
    
    DetailModel *model =_dataArray[indexPath.row];
    
    if (model.tempHeight <= 0) { //当前row的Cell高度没有计算过
        [_templateCell initModel:model];
        model.tempHeight = [_templateCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 1;
    }
    return model.tempHeight;
}

上面代码的systemLayoutSizeFittingSize函数,就是手动的调用Autolayout代码去计算填充完内容的Cell的高度,得到一个计算准确的Cell的高度。

Sometimes上面的代码运行的非常ok。有时候高度计算却有问题。仔细检查自己的Masonry也没有问题。为什么计算后返回的Cell高度是不正确的呢?纳闷!

最后。。。我添加了下面的代码的最后一行后,计算就正确了。可能是因为在Cell的ContentView里面去指定了UILabel的宽度,让autolayout对UILabel的高度计算产生了问题。

_myLabel = [UILabel new];
_myLabel.numberOfLines = 0;
_myLabel.font = FontNumber(17.0f);
_myLabel.textColor = COLOR_YELLOW;
[_backView addSubview:_myLabel];
[_myLabel mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(_backView.mas_top).offset(10);
    make.leading.equalTo(_backView).offset(5);
    make.trailing.equalTo(_backView).offset(-5);
}];

_myLabel.preferredMaxLayoutWidth = AppScreenWidth - 30;

需要手动指定这样一个UILabel的宽度preferredMaxLayoutWidth才可以。否则。。。

上面的技巧还有就是,计算过的row的Cell的高度会存储起来,下次不比再次计算。优化性能。


还有个Cell的计算方式,使用的纯代码的方式去计算UILabel的高度什么的,使用的是Frame布局。这个方法虽然有点out,但是还是非常有效的。不做详细描述。简要说明下:

在自定义Cell的类.h文件里,去创建一个对外的方式,取名叫做- (CGFloat)heightForCell;。在.m文件中创建一个maxHeight的CGFloat变量,用来记录在数据填充过程中的最大高度,即Cell的高度。

Cell创建一个- (void)initModel:(DetailModel *)model方法,这里有个地方注意。因为我在实际开发中遇到的一种例子是动态的Cell内容。所以我在每一次intiModel方法的时候把重用的那个Cell原有的控件给移除,在initModel方法中去创建新的控件去满足需求。

- (void)initModel:(DetailModel *)model {
    for (UIView *view in self.contentView.subviews) {
        [view removeFromSuperview];
    }

    //重新创建控件,添加到self.contentView中去。
    //...
    //计算新添加的控件在填充完数据后的最bottom的y值,赋值给self.maxHeight
    self.maxHeight = xxxx.bottom;
    //...
    //在填充数据的过程中,完成高度的计算
}


- (CGFloat)heightForCell {
    return self.maxHeight;
}

最后在TableView中的高度计算代码如下:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
	if (!_templateCell) {
	    _templateCell = [tableView dequeueReusableCellWithIdentifier:@"CELL_IDENTIFIER"];
	}

	DetailModel *model =_dataArray[indexPath.row];

	if (model.tempHeight <= 0) {
	    [_templateCell initModel:model];
	    model.tempHeight = [_templateCell heightForCell];
	}
	return model.tempHeight;
}

虽然是个不太简便的方法,但是有时候还是很靠谱的,特别是在处理Cell内容超级动态的问题上。。。

文章来自 http://skymonkey.cn/

高能广告区

暂无广告哦=^^=。继续看看其它文章吧!