iOS8+ UITableView自动计算cell高度并缓存
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];
}
效果图如下: