大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

MDKAutoLayoutHeight 自动化UITableviewCell高度计算工具

写这个工具的缘由

最近的业务需求要改一个很古老的界面:全部内容都是由frame实现,没有xib,没有autolayout,并且高度是通过手动计算每个控件的内容加起来返回的,而且根据网络请求的数据会有四种样式,听起来就头大

硬着头皮把界面改好后看到计算cell高度那里密密麻麻的if else真的下不了手了,想着能不能找到什么第三方可以做,接着想起了FDTemplateLayoutCell,百度出品的很著名的计算autolayout的工具,以前使用过,要大量修改原本的代码结构,把cellForRowAtIndexPath里的逻辑抽取出来太麻烦就没用,想了一下,通过layoutSuview就能拿到正确的cell了,那用这个来计算高度不就好了吗?于是乎就着手写了这个工具

一开始只是为了尽可能简单和尽可能不改动原有代码就能拿到高度,后来想想就算是autolayout,也可以简单用layoutIfNeed拿到正确的cell啊,于是就把autolayout也支持进去了,再后来发现计算frame的方式(需要注册一个最底部的view)可以用在不完整的autolayout,所以不管约束完不完整都支持了


另外也会有人问了,iOS11以后UITableView都默认开启estimatedRowHeight了,为什么还要手动计算高度呢?

答案当然是因为不好用啊

大部分的iOS11子版本上更新cell都会有动画问题,甚至当contentOffset不为CGPointZero的时候,tableView reloadData都会导致位置闪一下,只要把estimatedRowHeight设置为0就没有这些问题了,这个真的没办法,就算苹果默认开了这个也只能关了乖乖实现heightForRowAtIndexPath

所以这个工具还是很有用的


顺便求个星星!谢谢

功能

  • 高性能: 尽可能少的计算cell的高度,并且提供内存和磁盘缓存.
  • 自动更新: 基于数据模型hash的更新,当数据源或者tableview宽度有变化时会自动更新缓存.
  • 内存管理: 当系统提示内存不够的时候自动清理内存缓存.
  • 低侵入性: 基本上不需要改动任何代码结构就能使用这个框架,不像FDTemplateLayoutCell要根据他们的设计改动大量原有代码(还不好用).
  • 轻量级: 这个库的核心组件只有一个文件,一个类和一个tableview分类.
  • 容易使用: 只需要一行代码就能享受完全自动化的高度计算.

开源地址:
https://github.com/miku1958/UITableView-MDKAutoLayoutHeight

顺便求个星星!谢谢

使用方法

#import "UITableView+MDKAutoLayoutHeight.h"

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return [tableView.autoLayoutHeight heightForRowAtIndexPath:indexPath];
}

没了,就这样,超级简单对吧

如果需要对高度做什么事情(比如加个间距啊),可以用这个方法重新计算行高

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return [tableView.autoLayoutHeight heightForRowAtIndexPath:indexPath cachekey:acachekey handle:^CGFloat(__kindof UITableViewCell *cell, CGFloat height) {
      return height + 20;
    }];
}


如果你cell不是完全填充contentView的约束,你可以用MDKAutoLayoutRegisterHeight设置哪个view是最底部的view:

#import "UITableView+MDKAutoLayoutHeight.h"

@interface PartOfAutolayoutCell()
@proterty(nonatomic,strong)UIView *bottomView;
@end

@implementation PartOfAutolayoutCell
+(void)initialize{
    MDKAutoLayoutRegisterHeight(self, bottomView)
}
@end

并且如果实现了这个方法,那这套工具也能适用于用frame布局的cell,只要你是在layoutSubview中布局的(其他方式如放在sizeThatFit之类的应该也能用吧..大概),如果你遇到哪些地方用frame设置cell的控件位置后无效的,请告诉我

顺便一提,MDKAutoLayoutRegisterHeight() 是用C语言的宏实现的,如果你是用swift的话,需要用MDKAutoLayoutHeight.(registerHeight:_decisionView:#keyPath(view.bottom)) 填入最底部的view对应的属性名

在内存中缓存高度

如果在heightForRow方法中的acachekey返回nil或者@“”,你需要按照下面的内容手动返回一个cachekey

如果你需要缓存cell的高度到内存,只需要在cell中引入 <MDKTableviewCellCacheHeightDelegate> ,实现 -MDKModelHash 方法,返回一个具有唯一性的字符串给我就行,比如:

-(NSString *)MDKModelHash{
    return @(_model.ID).description;
}

如果你不是通过cell来处理model 而是直接在ViewController中处理,你可以把model的id传给cell再直接return id

如果是可能改变cell内容的话,可以把ID和决定cell内容是否有变化的标志符传给我,比如:

-(NSString *)MDKModelHash{
    return [NSString stringWithFormat:@"%@%@",@(_model.ID),@(_model.isDelete)];
}

等等等等.....

安装

pod 'UITableView-MDKAutoLayoutHeight'

如果需要把高度缓存到磁盘的话

pod 'UITableView-MDKAutoLayoutHeight/diskCache'

当tableview dealloc的时候就会把内存中的缓存写入磁盘 我提供了下面这些方法用来管理磁盘的缓存

import UITableView+MDKAutoLayoutHeightDiskCache.h

- (void)updateDiskCache;//用于某些会一直活着的tableview
- (void)removeCacheFor:(Class)cell;
- (void)removeAllCache;

已知问题

如果你dequeue cell的时候是这样的:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    return [tableView dequeueReusableCellWithIdentifier:@"Identifier" forIndexPath:indexPath];  
}

app就会crash,因为我是通过这个datasource方法获取cell的,而-dequeueReusableCellWithIdentifier:forIndexPath: 这个方法会调用 table.delegate -heightForRowAtIndexPath 所以就会陷入无限循环......解决办法是不用这个方法,改成-dequeueReusableCellWithIdentifier:qeueu cell

我实在没有想到一定要用这个方法的理由,如果有遇到什么情况是一定要用这个dequeue cell的话,请告诉我原因谢谢,我试试看有没有办法避开这个问题

感谢

部分用来确定contentView宽度的代码来自UITableView-FDTemplateLayoutCell