自动计算cell高度

首先申明一下我的这个代码是把第三方的UITableView+FDKeyedHeightCache.m 和 UITableView+FDTemplateLayoutCell抽离出来的,抽离后我个人认为比较简单,而且便于理解

1.先上一个图片


自定布局.gif

2.基本的代码是

首先 定义一个缓存的高度类别 UITableView+LeeKeyedHeightCache
.h 文件

//
//  UITableView+LeeKeyedHeightCache.h
//  动态计算cell 的高度
//
//  Created by apple on 16/6/13.
//  Copyright © 2016年 李重阳. All rights reserved.
//

#import <UIKit/UIKit.h>

/*缓存对象Cache**/
@interface LeeKeyedHeightCache : NSObject


@end


/*-------------------华丽的分界线-------------------**/
@interface UITableView (LeeKeyedHeightCache)


/*
 * 下面两个方法  用户是可以使用
 * 情况  1.当cell 改变的时候需要删除缓存高度
        2.当cell 删除、增加 等等,只要index的顺序改变了,都要重新改变
 **/
/* 清空某个Key的缓存的高度 **/
- (void)removeHeightCacheOfCellForKey:(NSString *)key;
/* 清空所有的缓存的高度 **/
- (void)removeAllHeightCacheOfCell;


/*
 * 下面两个方法不需要 用户使用的
 **/
/* 把某个高度 缓存到 key 中去**/
- (void)cacheCellHeight:(CGFloat)height forKey:(NSString *)key;
/* 从key 中取出某个高度 **/
- (CGFloat)heightOfCellForKey:(NSString *)key;



@end

.m文件

//
//  UITableView+LeeKeyedHeightCache.m
//  动态计算cell 的高度
//
//  Created by apple on 16/6/13.
//  Copyright © 2016年 李重阳. All rights reserved.
//


#import "UITableView+LeeKeyedHeightCache.h"
#import <objc/runtime.h>

@interface LeeKeyedHeightCache ()

/* 竖着的 高度缓存数组**/
@property (nonatomic, strong) NSCache *heightValuesForPortrait;
/* 全屏的 高度缓存数组**/
@property (nonatomic, strong) NSCache *heightValuesForLandscape;


/* 把某个高度 缓存到 key 中去**/
- (void)cacheHeight:(CGFloat)height forKey:(NSString *)key;
/* 从key 中取出某个高度 **/
- (CGFloat)heightForKey:(NSString *)key;
/* 清空某个Key的缓存的高度 **/
- (void)removeHeightForKey:(NSString *)key;
/* 清空所有的缓存的高度 **/
- (void)removeAllHeightCache;


@end

@implementation LeeKeyedHeightCache

#pragma mark - 初始化数据
/*初始化数据源 **/
- (NSCache *)heightValuesForPortrait {
    
    if (_heightValuesForPortrait == nil) {
        _heightValuesForPortrait = [[NSCache alloc]init];
    }
    return _heightValuesForPortrait;
}

- (NSCache *)heightValuesForLandscape {
    
    if (_heightValuesForLandscape == nil) {
        _heightValuesForLandscape = [[NSCache alloc]init];
    }
    return _heightValuesForLandscape;
}


#pragma mark - 私有方法
/* 判断出是 竖屏 还是 横屏中的某个值 **/
- (NSCache *)heightValuesForCurrentOrientation {
    
    return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ? self.heightValuesForPortrait:self.heightValuesForLandscape;
}

/* 通过number类型转换成CGFloat 类型**/
- (CGFloat)getFloatValueWithNumber:(NSNumber *)number {
    
#if CGFLOAT_IS_DOUBLE
    return [number doubleValue];
#else
    return [number floatValue];
#endif
    
}

#pragma mark - 公共接口

/* 把某个高度 缓存到 key 中去**/
- (void)cacheHeight:(CGFloat)height forKey:(NSString *)key {
    
    if (height > 0) {
        [self.heightValuesForCurrentOrientation setObject:@(height) forKey:key];
    }
    
}
/* 从key 中取出某个高度 **/
- (CGFloat)heightForKey:(NSString *)key {
    
    NSNumber * number = [self.heightValuesForCurrentOrientation objectForKey:key];
    return [self getFloatValueWithNumber:number];
}


/* 清空某个Key的缓存的高度 **/
- (void)removeHeightForKey:(NSString *)key {
    
    [self.heightValuesForPortrait  removeObjectForKey:key];
    [self.heightValuesForLandscape removeObjectForKey:key];
}
/* 清空所有的缓存的高度 **/
- (void)removeAllHeightCache {
    
    [self.heightValuesForPortrait  removeAllObjects];
    [self.heightValuesForLandscape removeAllObjects];
}

@end


/*-------------------华丽的分界线-------------------**/
/* 在类别中加入一个缓存对象**/
static const char LeeKeyedHeightCacheKey;


@implementation UITableView (LeeKeyedHeightCache)

- (LeeKeyedHeightCache *)keyedHeighCache {
    
    LeeKeyedHeightCache * cache = objc_getAssociatedObject(self, &LeeKeyedHeightCacheKey);
    if (cache == nil) {
        
        cache = [[LeeKeyedHeightCache alloc]init];
        objc_setAssociatedObject(self, &LeeKeyedHeightCacheKey, cache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return cache;
}



/* 把某个高度 缓存到 key 中去**/
- (void)cacheCellHeight:(CGFloat)height forKey:(NSString *)key {
    
    [self.keyedHeighCache cacheHeight:height forKey:key];
}
/* 从key 中取出某个高度 **/
- (CGFloat)heightOfCellForKey:(NSString *)key {
    
    return [self.keyedHeighCache heightForKey:key];
}

/* 清空某个Key的缓存的高度 **/
- (void)removeHeightCacheOfCellForKey:(NSString *)key {
    
    [self.keyedHeighCache removeHeightForKey:key];
}
/* 清空所有的缓存的高度 **/
- (void)removeAllHeightCacheOfCell {
    
    [self.keyedHeighCache removeAllHeightCache];
}




@end

接下来定义一个计算高度的类别UITableView+LeeAutoLayoutCell
.h文件

//
//  UITableView+LeeAutoLayoutCell.h
//  动态计算cell 的高度
//
//  Created by apple on 16/6/13.
//  Copyright © 2016年 李重阳. All rights reserved.
//

#import <UIKit/UIKit.h>
#import "UITableView+LeeKeyedHeightCache.h"

@interface UITableView (LeeAutoLayoutCell)

/* 返回自动布局cell的高度**/
- (CGFloat)heightForAutoLayoutCellWithIdentifier:(NSString *)identifier
                                     cacheForKey:(NSString *)key
                                   configuration:(void (^)(id cell))configuration;


@end

.m 文件


//
//  UITableView+LeeAutoLayoutCell.m
//  动态计算cell 的高度
//
//  Created by apple on 16/6/13.
//  Copyright © 2016年 李重阳. All rights reserved.
//

#import "UITableView+LeeAutoLayoutCell.h"
#import <objc/runtime.h>

static const char cellCacheKey;
@implementation UITableView (LeeAutoLayoutCell)

#pragma mark - 私有方法
/* 通过自动布局 来计算cell的高度 **/
- (CGFloat)heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id cell))configuration {
    
    UITableViewCell *cell = [self cellForReuseIdentifier:identifier];
    /*手动调用确保cell 在显示屏幕中 **/
    [cell prepareForReuse];
    /*需要把这个cell 传递出去 **/
    if (configuration) {
        configuration(cell);
    }
    /* 获得cell 的宽度 **/
    CGFloat contentViewWidth = CGRectGetWidth(self.frame);
    
    
    /* 修复cell 的宽度 **/
    if (cell.accessoryView) {
        contentViewWidth -= 16 + CGRectGetWidth(cell.accessoryView.frame);
    } else {
        static const CGFloat systemAccessoryWidths[] = {
            [UITableViewCellAccessoryNone] = 0,
            [UITableViewCellAccessoryDisclosureIndicator] = 34,
            [UITableViewCellAccessoryDetailDisclosureButton] = 68,
            [UITableViewCellAccessoryCheckmark] = 40,
            [UITableViewCellAccessoryDetailButton] = 48
        };
        contentViewWidth -= systemAccessoryWidths[cell.accessoryType];
    }
    
    if (contentViewWidth <= 0) {
        return 0;
    }
    
    CGSize fittingSize = CGSizeZero;
    
    /* 因为label 换行的时候 需要知道contentView 的最大的宽度 
     * 这个方法很good
     **/
    NSLayoutConstraint *tempWidthConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth];
    [cell.contentView addConstraint:tempWidthConstraint];
    // 自动布局的系统方法 计算高度
    fittingSize = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
    /* 计算完后 再删除 **/
    [cell.contentView removeConstraint:tempWidthConstraint];
    /* 修复 线 的1个像素 **/
    if (self.separatorStyle != UITableViewCellSeparatorStyleNone) {
        fittingSize.height += 1.0 / [UIScreen mainScreen].scale;
    }
    return fittingSize.height;
    
}

/* 获取 cell **/
- (UITableViewCell *)cellForReuseIdentifier:(NSString *)identifier {
    
    NSCache * cellCache = objc_getAssociatedObject(self, &cellCacheKey);
    if (cellCache == nil) {
        cellCache = [[NSCache alloc]init];
        objc_setAssociatedObject(self, &cellCacheKey, cellCache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    UITableViewCell *cell = [cellCache objectForKey:identifier];
    if (cell == nil) {
        cell = [self dequeueReusableCellWithIdentifier:identifier];
        /* cell 如果是nil 就报一个错误:提示用户应该要在tableView 注册cell**/
        NSAssert(cell != nil, @"Cell must be registered to table view for identifier - %@", identifier);
        cell.contentView.translatesAutoresizingMaskIntoConstraints = NO;
        [cellCache setObject:cell forKey:identifier];
    }
    return cell;
    
}

#pragma mark - 公共方法
/* 返回自动布局cell的高度**/
- (CGFloat)heightForAutoLayoutCellWithIdentifier:(NSString *)identifier
                                     cacheForKey:(NSString *)key
                                   configuration:(void (^)(id cell))configuration {
    
    if (identifier.length == 0 || key.length == 0) {
        return 0;
    }
    /*先从缓存中取 **/
    CGFloat cachedHeight = [self heightOfCellForKey:key];
    if (cachedHeight >0) {
        return cachedHeight;
    }else {
        //计算cell 的高度并且缓存进去
        CGFloat height = [self heightForCellWithIdentifier:identifier configuration:configuration];
        /* 缓存cell的高度**/
        [self cacheCellHeight:height forKey:key];
        return height;
    }
    
}

@end


3.计算高度和逻辑交互

计算高度

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    
    return [self.tableView heightForAutoLayoutCellWithIdentifier:@"MasCell" cacheForKey:[NSString stringWithFormat:@"MasCell%ld_%ld",indexPath.section,indexPath.row] configuration:^(MasCell *cell) {
        /*cell  需要重新布局 **/
        cell.model = self.dataArr[indexPath.row];
    }];
    
    
}

点击事件的处理

 /* 删除所有的 cell 的高度缓存**/
    [self.tableView removeAllHeightCacheOfCell];
    [self.tableView reloadData];

github 中的包内容

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

推荐阅读更多精彩内容