cell固定高度及cell不同高度的估算和计算。
一,如果Tableview的cell都是固定高度的,那么直接在初始化的时候进行赋值高度:
tableView.rowHeight = xxx;
一般的方法都是在代理里边返回了cell的高度,如果代理返回了cell的高度那么当前tableView.rowHeight设置会无效。代理方法为:
– (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath;
此方法会调用多次,如果固定cell的高度,建议直接用rowHeight属性进行设置。
代理方法适合多中高度cell的高度返回。
二,如果cell的高度不同,是需要进行计算cell的高度,如果用cell是用frame进行布局的话,那么cellHeight就得根据cell中的子空间大小加上下边距进行计算。计算起来着实麻烦。
如果cell是autolayout进行布局的话,cell的contentView提供了计算方法cell.contentView systemLayoutSizeFittingSize;
前提是cell.contentView中的布局中的约束合理,上下支撑严谨,否则计算出来的cell高度会是0。示例:
#import "DemoTableViewCell.h" #define ScreenFrame [[UIScreen mainScreen] bounds] #define ScreenWidth [[UIScreen mainScreen] bounds].size.width #define ScreenHeight [[UIScreen mainScreen] bounds].size.height @interface DemoTableViewCell () @property (nonatomic,strong)UIImageView *imgView; @property (nonatomic,strong)UILabel *contentLb; @end @implementation DemoTableViewCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if(self){ [self.contentView addSubview:self.imgView]; [self.contentView addSubview:self.contentLb]; NSLayoutConstraint *cont_0 = [NSLayoutConstraint constraintWithItem:self.imgView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeTop multiplier:1.0 constant:10.0]; NSLayoutConstraint *cont_1 = [NSLayoutConstraint constraintWithItem:self.imgView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]; NSLayoutConstraint *cont_2 = [NSLayoutConstraint constraintWithItem:self.imgView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:40.0]; NSLayoutConstraint *cont_3 = [NSLayoutConstraint constraintWithItem:self.imgView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:40.0]; NSArray *imgConstraints = [NSArray arrayWithObjects:cont_0,cont_1,cont_2,cont_3, nil]; [self.contentView addConstraints:imgConstraints]; // NSLayoutConstraint *cont_l_0 = [NSLayoutConstraint constraintWithItem:self.contentLb attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.imgView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:10.0]; NSLayoutConstraint *cont_l_1 = [NSLayoutConstraint constraintWithItem:self.contentLb attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:10.0]; NSLayoutConstraint *cont_l_2 = [NSLayoutConstraint constraintWithItem:self.contentLb attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeRight multiplier:1.0 constant:-10.0]; NSLayoutConstraint *cont_l_3 = [NSLayoutConstraint constraintWithItem:self.contentLb attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-10.0]; NSArray *lbConstraints = [NSArray arrayWithObjects:cont_l_0,cont_l_1,cont_l_2,cont_l_3, nil]; [self.contentView addConstraints:lbConstraints]; } return self; } - (UIImageView *)imgView { if(!_imgView){ _imgView = [[UIImageView alloc]init]; [_imgView setTranslatesAutoresizingMaskIntoConstraints:NO]; _imgView.backgroundColor = [UIColor orangeColor]; } return _imgView; } - (UILabel *)contentLb { if(!_contentLb){ _contentLb = [[UILabel alloc]init]; [_contentLb setTranslatesAutoresizingMaskIntoConstraints:NO]; _contentLb.font = [UIFont systemFontOfSize:16.0]; _contentLb.preferredMaxLayoutWidth = ScreenWidth - 20; _contentLb.numberOfLines = 0; } return _contentLb; } - (void)settingCellWithString:(NSString *)str { if(str != nil){ self.contentLb.text = str; } }
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _tableView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain]; _tableView.delegate = self; _tableView.dataSource = self; _tableView.rowHeight = UITableViewAutomaticDimension; _tableView.estimatedRowHeight = 200.0; [self.view addSubview:_tableView]; _dataArray = [NSMutableArray arrayWithObjects:@"[[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain]", @"陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖", @"陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖", @"陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖", @"陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖陈:缘缘份份聚散 命运在转谁能预料或尚有明天黎:多少的恋爱是温暖", @"多少的恋爱是温暖", nil]; [_tableView reloadData]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _dataArray.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *DemoTableViewCellID = @"DemoTableViewCellID"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:DemoTableViewCellID]; if(cell == nil){ cell = [[DemoTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:DemoTableViewCellID]; } [(DemoTableViewCell *)cell settingCellWithString:_dataArray[indexPath.row]]; return cell; }
那么当前cell适配是做了高度自适应,但是如果数据量较大,在上下滑动的时候,每次系统都得去进行计算高度并且返回然后渲染,这样比较消耗系统资源,需将高度数据进行缓存。
三,AutoLayout布局的cell进行高度计算并且缓存高度。
缓存方案,通过cell方法systemLayoutSizeFittingSize计算高度,缓存到数据或者Model中,当前是在一个字典中。
首先删除初始化tableview中的rowHeight:
_tableView.rowHeight = UITableViewAutomaticDimension;
缓存如下,为tableview增加了一个分类,保存缓存数据:
在返回cell的代理方法中计算cell缓存,在返回高度时获取缓存。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CGFloat cellHeight = [tableView cacheHeightWithIndexPath:indexPath]; NSLog(@"%lf",cellHeight); return cellHeight; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *DemoTableViewCellID = @"DemoTableViewCellID"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:DemoTableViewCellID]; if(cell == nil){ cell = [[DemoTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:DemoTableViewCellID]; } [(DemoTableViewCell *)cell settingCellWithString:_dataArray[indexPath.row]]; //进行缓存 [tableView cacheHeightWithCell:cell contentWidth:self.view.bounds.size.width indexPath:indexPath]; return cell; }
@interface UITableView (CellHeight) @property (nonatomic,strong,readonly)NSMutableDictionary *autoHeightDic; //缓存高度 - (void)cacheHeightWithCell:(UITableViewCell *)cell contentWidth:(CGFloat)width indexPath:(NSIndexPath *)indexPath; //获取高度 - (CGFloat)cacheHeightWithIndexPath:(NSIndexPath *)indexPath; @end // #import "UITableView+CellHeight.h" #import <objc/runtime.h> @implementation UITableView (CellHeight) - (void)setAutoHeightDic:(NSMutableDictionary * _Nonnull)autoHeightDic { objc_setAssociatedObject(self, @selector(autoHeightDic), autoHeightDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSMutableDictionary *)autoHeightDic { NSMutableDictionary *_autoHeightDic = objc_getAssociatedObject(self, @selector(autoHeightDic)); if(_autoHeightDic == nil){ _autoHeightDic = [NSMutableDictionary dictionary]; objc_setAssociatedObject(self, @selector(autoHeightDic), _autoHeightDic, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return _autoHeightDic; } //缓存高度 - (void)cacheHeightWithCell:(UITableViewCell *)cell contentWidth:(CGFloat)width indexPath:(NSIndexPath *)indexPath; { //设置字典key NSString *cacheKey = [self cacheKeyWithIndexPath:indexPath]; //判断是否已经有缓存 NSNumber *cacheHeight = [self.autoHeightDic objectForKey:cacheKey]; if (cacheHeight) { return; } CGFloat height = [cell systemLayoutSizeFittingSize:CGSizeMake(width, 0) withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:UILayoutPriorityFittingSizeLevel].height; cacheHeight = [NSNumber numberWithFloat:height]; [self.autoHeightDic setValue:cacheHeight forKey:cacheKey]; } //获取高度 - (CGFloat)cacheHeightWithIndexPath:(NSIndexPath *)indexPath { NSString *cacheKey = [self cacheKeyWithIndexPath:indexPath]; NSNumber *cacheHeight = [self.autoHeightDic objectForKey:cacheKey]; if (cacheHeight) { return [cacheHeight floatValue]; } return UITableViewAutomaticDimension; } - (NSString *)cacheKeyWithIndexPath:(NSIndexPath *)indexPath { return [NSString stringWithFormat:@"%ld-%ld", indexPath.section, indexPath.row]; } @end
比较复杂的cell,子控件比较多,每个子控件在计算整体cell的时候,也是需要再次进行计算高度,这样对CPU的资源消耗也是比较大的,所以需要可以将上述字典中的value作为一个model,保存每一个控件的高度作为缓存。
对于frame布局的cell,高度缓存可以在请求完数据进行缓存,通过请求完的数据,例如一个Model,去计算cell里边的动态视图和静态视图的高度,加起来则是整个cell的高度,然后进行缓存。参考:
https://www.jianshu.com/p/af4bc69839d8
如果有此tableview中有多种类型的cell,例如发送消息时,有文字冒泡cell,有图片cell,还有一些名片信息cell,每一种布局在代理方法中返回不同的cell,最好最缓存机制,否则会在刷新或者下拉加载时产生卡顿,跳屏的现象。
在autolayout布局计算cell高度时,必须确定cell中contentView的宽度。