iOS TableViewcell 高度计算+动态高度

开发中tableView的cell高度分为写死的高度和动态的高度,下面是本人自己总结的一些操作。


固定cell高度:

cell高度分为等高和不等高,对于等高的情况,很简单,直接设置tableView.rowHeight即可,或者是在代理方法里heightForRowAtIndexPath: 里返回一个固定值就行了;在实际应用中,cell的高度往往是可变的,需要根据显示内容大小或者是屏幕尺寸动态变化。


不固定cell高度

对于不固定cell高度,总共种情况

    1. tableView代理返回高度模式
    1. AutoLayout自动计算模式

1. TableView代理返回高度模式

介绍:

可通过frame进行布局,属于最基础的布局方式了,代码简单。
缺点cell复杂,存在内容动态显示时,代码比较难以维护,视图布局需要一套统一的布局链, 否则一不小心一团遭
也可通过Masonry进行布局,优点是约束好了后,修改一个视图布局,相关视图自动改变位置。
这种模式设置cell高度的一般的方法是给模型增加一个辅助属性的cellHeight

具体实现:

直接在模型里面加上cellheight高度属性,因为模型里面的数据就决定了cell的高度,重写cellheight的getter方法,里面直接利用数据把高度计算出来,赋给_cellheight。这样只需要在代理方法中返回高度即可。
计算高度最好给cell计算,因为cell内部有计算所需要的字体,宽度,间距等数据。

  • model get方法中计算高度 并缓存
#import "SSModel.h"

@implementation SSModel

- (CGFloat)cellHeight {
  if (_cellHeight == 0) {
        _cellHeight = [SSCell calculateCellHeight:self];
    }
    return _cellHeight
}
@end
  • cell负责根据模型数据计算高度
#import "SSCell.h"
#define kLabelWidth = 200
#define kLabelTop = 10
#define kLabelBottom = 10
@interface  SSCell()

@end

@implementation SSCell

+ (CGFloat)calculateCellHeight:(SSModel *)model {
     //  伪代码 通过模型数据计算高度
    CGFloat labelHeight = [model.text boundingRectWithSize:xxx];
    CGFloat  height =  kLabelTop + labelHeight  + kLabelBottom;
    return  height;
}

@end
  • tableView代理返回缓存的高度
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    SSModel *model = _dataArray[indexPath.row];
    return model.cellHeight;//高度模型内已经计算。
  }


2. AutoLayout自动计算模式

iOS8以后有一个自动计算cell高度的方法。
这个方法不需要代理方法返回高度了,直接通过AutoLayout自动计算。

核心思想

  1. 首先要让自动适应内容的子控件与cell建立联系。比如在Xib中,之前我们约束一个高度可变的label,通过约束它距左边距上边的距离,在约束宽度后这个label,已经确定了,此时我们再将label的底部和cell的底部建立约束关系。
  2. 添加cell估算代码和cell高度的代码
    tableView.estimatedRowHeight = 44;
    tableView.rowHeight = UITableViewAutomaticDimension;
    estimatedRowHeight高度最好设置成最贴近cell本身高度,因为自动估算高度不准可能会导致右侧滚动条不精确,跳动。否则就隐藏掉。。
  3. 不用代理方法返回cell高度,运行程序,cell高度自动适应了。

这个方法最核心主要在让子控件和父控件建立联系。

Example
  • xib:
    label高度不用设置,左右约束,top,bottom约束即可。


  • 代码:(Masonry)

[label mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.contentView.mas_top).offset(10);
        make.left.right.equalTo(self.contentView);
        make.bottom.equalTo(self.contentView.mas_bottom).offset(-20);
    }];


进阶-cell动态改变高度

针对Masonry布局的cell。 xib cell动态改变高度需要将高度约束拖入代码。
  • remake方式
- (void)change {
    [label mas_remakeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.contentView.mas_top).offset(30); //改成30
        make.left.right.equalTo(self.contentView);
        make.bottom.equalTo(self.contentView.mas_bottom).offset(-20);
    }];
  //  回调tableView reloadData
}

remake方式会清除之前该所有的约束。对于布局改动较大的,比较方便。如果只针对一个约束进行更新。使用update

  • update方式
- (void)change {
    [label mas_updateConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.contentView.mas_top).offset(30); //改成
    }];
  //  回调tableView reloadData
}

update方式更新只能更新之前存在的约束。
所以update内写
make.top.equalTo(self.otherView.mas_top).offset(30); 是无效的。因为label的顶部距离cell顶部label顶部距离otherView顶部是两个约束。这种情况可以使用remake,但是不够优雅,因为要重新书写一遍其他的约束,代码冗余。 还有一种优雅的方式:约束activate/deactivate+优先级

  • 约束activate/deactivate+优先级
    我们可以使用Masonry提供的api,控制一个约束是否激活。
@interface SSCell()
@property(nonatomic, strong) MASConstraint *labelTopConstraint;
@end

- (void) createConstraint {
    [self.label mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.contentView.mas_top).offset(10).priority(100);
        self.labelTopConstraint = make.top.equalTo(self.otherView.mas_top).offset(30);
        make.left.right.equalTo(self.contentView);
        make.bottom.equalTo(self.contentView.mas_bottom).offset(-20).priority(300);
    }];
    [self. labelTopConstraint deactivate]; // 失效
}

- (void)change {
    [self. labelTopConstraint activate]; // 生效
 //  回调tableView reloadData
}

上述代码控制label顶部的约束有两个:

  • 约束A:顶部距cell顶部10
  • 约束B:顶部距离otherView顶部20
    约束B刚开始失效,所以约束A产生作用。
    chang事件后,约束B生效,label顶部同时出现两个约束,由于约束A优先级低,所以约束B产生作用。cell高度就动态改变了。

说明: 不管是什么方式动态改变了cell的高度,都要调用[tableView reloadData] 整体刷新tableView,否则不会重绘,改变不能立即生效,需要滑动复用了才行。

可能遇到的问题

  • 约束警告问题

有时会爆约束警告,一般就是同一尺寸或者同一方向进行了过约束问题,可以在打印台查看问题或者打符号断点。如果实在找不到约束设置的问题,可以尝试将其中一个尺寸的约束优先级设低。

[label mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.contentView.mas_top).offset(10);
        make.left.right.equalTo(self.contentView);
        make.bottom.equalTo(self.contentView.mas_bottom).offset(-20).priority(300);
    }];
  • masonry布局cell 刷新后tableView抖动问题。

使用autoLayout方式自动撑大的cell,重新reloadData的时候,tableView偏移量可能发生改变或者抖动。


#pragma mark - 解决动态cell高度 reloadData刷新抖动的问题
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
   _estimatedRowHeightCache[indexPath] = @(cell.frame.size.height);
}

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
      return [_estimatedRowHeightCache[indexPath] floatValue] ?:1.0;
}

将每次的cell高度按indexPath或者indexPath+cell类型的方式缓存下来,在上述方法返回。 第一次缓存高度为0时必须返回一个不为0的高度,否则cell显示不出来。

  • cell为XIB时,label不换行,滚动刷新后才换行的问题。

要将label的numberOfLines属性设置为0,不然不会自动分行。
还要设置下面代码;

- (void)awakeFromNib {
   [self layoutIfNeed];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容