首先申明一下我的這個(gè)代碼是把第三方的UITableView+FDKeyedHeightCache.m 和 UITableView+FDTemplateLayoutCell抽離出來的,抽離后我個(gè)人認(rèn)為比較簡(jiǎn)單,而且便于理解
1.先上一個(gè)圖片
2.基本的代碼是
首先 定義一個(gè)緩存的高度類別 UITableView+LeeKeyedHeightCache
.h 文件
//
// UITableView+LeeKeyedHeightCache.h
// 動(dòng)態(tài)計(jì)算cell 的高度
//
// Created by apple on 16/6/13.
// Copyright ? 2016年 李重陽(yáng). All rights reserved.
//
#import <UIKit/UIKit.h>
/*緩存對(duì)象Cache**/
@interface LeeKeyedHeightCache : NSObject
@end
/*-------------------華麗的分界線-------------------**/
@interface UITableView (LeeKeyedHeightCache)
/*
* 下面兩個(gè)方法 用戶是可以使用
* 情況 1.當(dāng)cell 改變的時(shí)候需要?jiǎng)h除緩存高度
2.當(dāng)cell 刪除咳促、增加 等等,只要index的順序改變了于颖,都要重新改變
**/
/* 清空某個(gè)Key的緩存的高度 **/
- (void)removeHeightCacheOfCellForKey:(NSString *)key;
/* 清空所有的緩存的高度 **/
- (void)removeAllHeightCacheOfCell;
/*
* 下面兩個(gè)方法不需要 用戶使用的
**/
/* 把某個(gè)高度 緩存到 key 中去**/
- (void)cacheCellHeight:(CGFloat)height forKey:(NSString *)key;
/* 從key 中取出某個(gè)高度 **/
- (CGFloat)heightOfCellForKey:(NSString *)key;
@end
.m文件
//
// UITableView+LeeKeyedHeightCache.m
// 動(dòng)態(tài)計(jì)算cell 的高度
//
// Created by apple on 16/6/13.
// Copyright ? 2016年 李重陽(yáng). All rights reserved.
//
#import "UITableView+LeeKeyedHeightCache.h"
#import <objc/runtime.h>
@interface LeeKeyedHeightCache ()
/* 豎著的 高度緩存數(shù)組**/
@property (nonatomic, strong) NSCache *heightValuesForPortrait;
/* 全屏的 高度緩存數(shù)組**/
@property (nonatomic, strong) NSCache *heightValuesForLandscape;
/* 把某個(gè)高度 緩存到 key 中去**/
- (void)cacheHeight:(CGFloat)height forKey:(NSString *)key;
/* 從key 中取出某個(gè)高度 **/
- (CGFloat)heightForKey:(NSString *)key;
/* 清空某個(gè)Key的緩存的高度 **/
- (void)removeHeightForKey:(NSString *)key;
/* 清空所有的緩存的高度 **/
- (void)removeAllHeightCache;
@end
@implementation LeeKeyedHeightCache
#pragma mark - 初始化數(shù)據(jù)
/*初始化數(shù)據(jù)源 **/
- (NSCache *)heightValuesForPortrait {
if (_heightValuesForPortrait == nil) {
_heightValuesForPortrait = [[NSCache alloc]init];
}
return _heightValuesForPortrait;
}
- (NSCache *)heightValuesForLandscape {
if (_heightValuesForLandscape == nil) {
_heightValuesForLandscape = [[NSCache alloc]init];
}
return _heightValuesForLandscape;
}
#pragma mark - 私有方法
/* 判斷出是 豎屏 還是 橫屏中的某個(gè)值 **/
- (NSCache *)heightValuesForCurrentOrientation {
return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ? self.heightValuesForPortrait:self.heightValuesForLandscape;
}
/* 通過number類型轉(zhuǎn)換成CGFloat 類型**/
- (CGFloat)getFloatValueWithNumber:(NSNumber *)number {
#if CGFLOAT_IS_DOUBLE
return [number doubleValue];
#else
return [number floatValue];
#endif
}
#pragma mark - 公共接口
/* 把某個(gè)高度 緩存到 key 中去**/
- (void)cacheHeight:(CGFloat)height forKey:(NSString *)key {
if (height > 0) {
[self.heightValuesForCurrentOrientation setObject:@(height) forKey:key];
}
}
/* 從key 中取出某個(gè)高度 **/
- (CGFloat)heightForKey:(NSString *)key {
NSNumber * number = [self.heightValuesForCurrentOrientation objectForKey:key];
return [self getFloatValueWithNumber:number];
}
/* 清空某個(gè)Key的緩存的高度 **/
- (void)removeHeightForKey:(NSString *)key {
[self.heightValuesForPortrait removeObjectForKey:key];
[self.heightValuesForLandscape removeObjectForKey:key];
}
/* 清空所有的緩存的高度 **/
- (void)removeAllHeightCache {
[self.heightValuesForPortrait removeAllObjects];
[self.heightValuesForLandscape removeAllObjects];
}
@end
/*-------------------華麗的分界線-------------------**/
/* 在類別中加入一個(gè)緩存對(duì)象**/
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;
}
/* 把某個(gè)高度 緩存到 key 中去**/
- (void)cacheCellHeight:(CGFloat)height forKey:(NSString *)key {
[self.keyedHeighCache cacheHeight:height forKey:key];
}
/* 從key 中取出某個(gè)高度 **/
- (CGFloat)heightOfCellForKey:(NSString *)key {
return [self.keyedHeighCache heightForKey:key];
}
/* 清空某個(gè)Key的緩存的高度 **/
- (void)removeHeightCacheOfCellForKey:(NSString *)key {
[self.keyedHeighCache removeHeightForKey:key];
}
/* 清空所有的緩存的高度 **/
- (void)removeAllHeightCacheOfCell {
[self.keyedHeighCache removeAllHeightCache];
}
@end
接下來定義一個(gè)計(jì)算高度的類別UITableView+LeeAutoLayoutCell
.h文件
//
// UITableView+LeeAutoLayoutCell.h
// 動(dòng)態(tài)計(jì)算cell 的高度
//
// Created by apple on 16/6/13.
// Copyright ? 2016年 李重陽(yáng). All rights reserved.
//
#import <UIKit/UIKit.h>
#import "UITableView+LeeKeyedHeightCache.h"
@interface UITableView (LeeAutoLayoutCell)
/* 返回自動(dòng)布局cell的高度**/
- (CGFloat)heightForAutoLayoutCellWithIdentifier:(NSString *)identifier
cacheForKey:(NSString *)key
configuration:(void (^)(id cell))configuration;
@end
.m 文件
//
// UITableView+LeeAutoLayoutCell.m
// 動(dòng)態(tài)計(jì)算cell 的高度
//
// Created by apple on 16/6/13.
// Copyright ? 2016年 李重陽(yáng). All rights reserved.
//
#import "UITableView+LeeAutoLayoutCell.h"
#import <objc/runtime.h>
static const char cellCacheKey;
@implementation UITableView (LeeAutoLayoutCell)
#pragma mark - 私有方法
/* 通過自動(dòng)布局 來計(jì)算cell的高度 **/
- (CGFloat)heightForCellWithIdentifier:(NSString *)identifier configuration:(void (^)(id cell))configuration {
UITableViewCell *cell = [self cellForReuseIdentifier:identifier];
/*手動(dòng)調(diào)用確保cell 在顯示屏幕中 **/
[cell prepareForReuse];
/*需要把這個(gè)cell 傳遞出去 **/
if (configuration) {
configuration(cell);
}
/* 獲得cell 的寬度 **/
CGFloat contentViewWidth = CGRectGetWidth(self.frame);
/* 修復(fù)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;
/* 因?yàn)閘abel 換行的時(shí)候 需要知道contentView 的最大的寬度
* 這個(gè)方法很good
**/
NSLayoutConstraint *tempWidthConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth];
[cell.contentView addConstraint:tempWidthConstraint];
// 自動(dòng)布局的系統(tǒng)方法 計(jì)算高度
fittingSize = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
/* 計(jì)算完后 再刪除 **/
[cell.contentView removeConstraint:tempWidthConstraint];
/* 修復(fù) 線 的1個(gè)像素 **/
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 就報(bào)一個(gè)錯(cuò)誤:提示用戶應(yīng)該要在tableView 注冊(cè)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 - 公共方法
/* 返回自動(dòng)布局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 {
//計(jì)算cell 的高度并且緩存進(jìn)去
CGFloat height = [self heightForCellWithIdentifier:identifier configuration:configuration];
/* 緩存cell的高度**/
[self cacheCellHeight:height forKey:key];
return height;
}
}
@end
3.計(jì)算高度和邏輯交互
計(jì)算高度
- (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];
}];
}
點(diǎn)擊事件的處理
/* 刪除所有的 cell 的高度緩存**/
[self.tableView removeAllHeightCacheOfCell];
[self.tableView reloadData];