美文网首页
UITableViewCell高度自适应

UITableViewCell高度自适应

作者: fmxccccc | 来源:发表于2017-03-26 23:20 被阅读507次

    前言

    其实这个关于 UITableViewCell 高度自适应的文章在网上也有很多,这里推荐下 sunnyxx单身的一篇文章,本文也是根据他的思路自己琢磨出来的,并且这也是一个比较古老的话题了,正好最近在项目中也需要去做一些高度自适应的东西,在这里做一个记录,记录下在 Masonry 自动布局中的 Cell 高度自适应

    Let's Start

    整个高度自适应的核心就是这段代码

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    

    在这个代码中我们需要返回 cell 的高度,默认都是44,那么在这个方法中我们还要用到的一个核心代是:

    [someView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
    

    这个方法返回的是一个 CGSize 对象,我们需要用到的就是这个返回值的 height 值,这个方法会根据 someView 中的子视图的约束来计算出一个新的size,那么如果你的约束条件有冲突的话这个方法返回的值会有问题,这个方法中还需要传入一个参数,这个参数类型有两种:

    • UILayoutFittingCompressedSize

    • UILayoutFittingExpandedSize

    根据自己的理解,传入第一种参数会得出一个大小适中的尺寸,那么传入第二种参数的话的到的值应该比第一种参数的值要大。

    介绍完核心的思路后,我们来看一下大致的代码,首先是自定义的 cell

     @interface DemoCell : UITableViewCell
    
    - (void)setDemoCellType:(NSString *)type info:(NSString *)info;
    
    @end
    
    @interface DemoCell ()
    
    @property (nonatomic, strong) UILabel *typeLabel;
    @property (nonatomic, strong) UILabel *infoLabel;
    
    @end
    
    @implementation DemoCell
    
    - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
    {
        if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
            [self commonInit];
        }
        
        return self;
    }
    
    #pragma mark - Public Mehods
    
    - (void)setDemoCellType:(NSString *)type info:(NSString *)info
    {
        self.typeLabel.text = type;
        self.infoLabel.text = info;
    }
    
    #pragma mark - Customized
    
    - (void)commonInit
    {
        [self.contentView addSubview:self.typeLabel];
        [self.typeLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            
            make.top.equalTo(self.contentView.mas_top).offset(16);
            make.left.equalTo(self.contentView.mas_left).offset(16);
            make.width.mas_equalTo(100);
            
        }];
        
        [self.contentView addSubview:self.infoLabel];
        [self.infoLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            
            make.top.equalTo(self.typeLabel.mas_bottom).offset(10);
            make.left.equalTo(self.typeLabel.mas_left);
            make.right.equalTo(self.contentView.mas_right).offset(-16);
            make.bottom.equalTo(self.contentView.mas_bottom).offset(-5);
            
        }];
    }
    
    #pragma mark - Getters
    
    - (UILabel *)typeLabel
    {
        if (_typeLabel == nil)
        {
            _typeLabel = [[UILabel alloc] initWithFrame:CGRectZero];
            _typeLabel.textColor = [UIColor blackColor];
            _typeLabel.backgroundColor = [UIColor redColor];
        }
        
        return _typeLabel;
    }
    
    - (UILabel *)infoLabel
    {
        if (_infoLabel == nil)
        {
            _infoLabel = [[UILabel alloc] initWithFrame:CGRectZero];
            _infoLabel.textColor = [UIColor blackColor];
            _infoLabel.backgroundColor = [UIColor yellowColor];
            _infoLabel.numberOfLines = 0;
        }
        
        return _infoLabel;
    }
    
    @end
    

    这里代码都很简单,使用了 Masonry 布局,外部可调用 - (void)setDemoCellType:(NSString *)type info:(NSString *)info;来进行 cell 内容的赋值。

    接下来就是 tableView 的代理方法中我们需要做的事情

    - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        CGFloat height = 0;
        
        NSString *info = _dataArray[indexPath.row][kCellInfoText];
        NSString *type = _dataArray[indexPath.row][kCellTypeText];
        [self.demoCell setDemoCellType:type info:info];
         height = [self.demoCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
    
        return height + 1;    
    }
    

    这里要注意的一点是 self.demoCell 只是用来计算的,它并没有显示在 tableView 上,还有这里是以 cell 中的 contentView 来计算,并且在最后返回的时候需要 +1 因为 cell 本身的高度要比它 contentView 的高度要高 1。

    其实到这里,我们想要的效果就已经达到了,但是如果数据比较多并且很复杂的时候呢,这个方法会很耗时并且有可能会掉帧,掉帧的问题我现在还没有研究,主要说一下耗时,我的数据源大致长这个样子

        _dataArray = @[@{kCellInfoText:@"1\n2\n3\n4\n5\n6", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"123456789012345678901234567890", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1\n2", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1\n2\n3", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1\n2\n3\n4\n5\n6", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"123456789012345678901234567890", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1\n2", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1\n2\n3", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1", kCellTypeText:@"type..."},
                       @{kCellInfoText:@"1\n2\n3\n4\n5\n6", kCellTypeText:@"type..."},....后面还有很多类似的就不贴出来了];
    

    在说这个问题之前要先说下使用到的工具 Xcode 自带的 instrument 中的 Time Profiler 首先我们需要设置一下

    设置

    图中的勾选项依次是(参考自网络)

    • Separate by Thread: 显示每个线程

    • Invert Call Tree: 从上倒下跟踪堆栈,例如:FuncA{FunB{FunC}} 勾选此项后堆栈以C->B-A 把调用层级最深的C显示在最外面

    • Hide System Libraries: 勾选此项你会显示你app的代码,这是非常有用的. 因为通常你只关心cpu花在自己代码上的时间不是系统上的

    还有

    • Flatten Recursion: 递归函数, 每个堆栈跟踪一个条目

    • Top Functions: 一个函数花费的时间直接在该函数中的总和,以及在函数调用该函数所花费的时间的总时间。因此,如果函数A调用B,那么A的时间报告在A花费的时间加上B花费的时间,这非常真有用,因为它可以让你每次下到调用堆栈时挑最大的时间数字,归零在你最耗时的方法。

    那么接下来就可以开始测试了,测试数据如下:

    测试1

    当我们来回滑动的时候,它耗时会不断的在增加,因为每次滑动都会去计算一次高度,这显然不是我们想要的,为了解决这个问题,我们需要一个缓存来存储每一个 cell 的高度,那我们修改代码如下:

        if (self.cache[@(indexPath.row)])
        {
            height = [self.cache[@(indexPath.row)] floatValue];
        }
        else
        {
            NSString *info = _dataArray[indexPath.row][kCellInfoText];
            NSString *type = _dataArray[indexPath.row][kCellTypeText];
            [self.demoCell setDemoCellType:type info:info];
            height = [self.demoCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
            [self.cache setObject:@(height) forKey:@(indexPath.row)];
        }
        
        return height + 1;
    

    我们将每个 cellindexPatch.row 作为 key 值来将高度存储到缓存中,第一次计算过后,每次都去缓存中去取。

    测试2

    对比两次我们的结果却大不一样。有关帧率的问题等我研究后,在写一篇文章来记录下。

    相关文章

      网友评论

          本文标题:UITableViewCell高度自适应

          本文链接:https://www.haomeiwen.com/subject/gullottx.html