zyds/ProductApp/Pods/SDAutoLayout/SDAutoLayoutDemo/SDAutoLayout/UITableView+SDAutoTableView...

538 lines
20 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// UITableView+SDAutoTableViewCellHeight.m
// SDAutoLayout 测试 Demo
//
// Created by aier on 15/11/1.
// Copyright © 2015年 gsd. All rights reserved.
//
/*
*********************************************************************************
* *
* 在您使用此自动布局库的过程中如果出现bug请及时以以下任意一种方式联系我们我们会及时修复bug并 *
* 帮您解决问题。 *
* QQ : 2689718696(gsdios) *
* Email : gsdios@126.com *
* GitHub: https://github.com/gsdios *
* 新浪微博:GSD_iOS *
* *
*********************************************************************************
*/
#import "UITableView+SDAutoTableViewCellHeight.h"
#import <objc/runtime.h>
@interface SDCellAutoHeightManager ()
@property (nonatomic, weak) UITableView *modelTableview;
@end
@implementation SDCellAutoHeightManager
{
NSMutableDictionary *_cacheDictionary;
NSMutableDictionary *_modelCellsDict;
}
- (instancetype)init
{
if (self = [super init]) {
[self setup];
}
return self;
}
- (instancetype)initWithCellClass:(Class)cellClass tableView:(UITableView *)tableView
{
if (self = [super init]) {
[self setup];
self.modelTableview = tableView;
[self registerCellWithCellClass:cellClass];
}
return self;
}
- (instancetype)initWithCellClasses:(NSArray *)cellClassArray tableView:(UITableView *)tableView
{
if (self = [super init]) {
[self setup];
self.modelTableview = tableView;
[cellClassArray enumerateObjectsUsingBlock:^(Class obj, NSUInteger idx, BOOL *stop) {
[self registerCellWithCellClass:obj];
}];
}
return self;
}
- (void)setup
{
_cacheDictionary = [NSMutableDictionary new];
_modelCellsDict = [NSMutableDictionary new];
}
- (void)registerCellWithCellClass:(Class)cellClass
{
[_modelTableview registerClass:cellClass forCellReuseIdentifier:NSStringFromClass(cellClass)];
self.modelCell = [_modelTableview dequeueReusableCellWithIdentifier:NSStringFromClass(cellClass)];
if (!self.modelCell.contentView.subviews.count) {
NSString *path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"%@.nib", NSStringFromClass(cellClass)] ofType:nil];
if (path) {
self.modelCell = nil;
[_modelTableview registerNib:[UINib nibWithNibName:NSStringFromClass(cellClass) bundle:nil] forCellReuseIdentifier:NSStringFromClass(cellClass)];
self.modelCell = [_modelTableview dequeueReusableCellWithIdentifier:NSStringFromClass(cellClass)];
}
}
if (self.modelCell) {
[_modelCellsDict setObject:self.modelCell forKey:NSStringFromClass(cellClass)];
}
}
+ (instancetype)managerWithCellClass:(Class)cellClass tableView:(UITableView *)tableView
{
SDCellAutoHeightManager *manager = [[self alloc] initWithCellClass:cellClass tableView:tableView];
return manager;
}
- (UITableViewCell *)modelCell
{
if (_modelCell.contentView.tag != kSDModelCellTag) {
_modelCell.contentView.tag = kSDModelCellTag;
}
return _modelCell;
}
- (NSDictionary *)heightCacheDict
{
return _cacheDictionary;
}
- (void)clearHeightCache
{
[_cacheDictionary removeAllObjects];
[_subviewFrameCacheDict removeAllObjects];
}
- (NSString *)cacheKeyForIndexPath:(NSIndexPath *)indexPath
{
return [NSString stringWithFormat:@"%ld-%ld", (long)indexPath.section, (long)indexPath.row];
}
- (void)clearHeightCacheOfIndexPaths:(NSArray *)indexPaths
{
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
NSString *cacheKey = [self cacheKeyForIndexPath:indexPath];
[_cacheDictionary removeObjectForKey:cacheKey];
[_subviewFrameCacheDict removeObjectForKey:cacheKey];
}];
}
- (void)deleteThenResetHeightCache:(NSIndexPath *)indexPathToDelete
{
NSString *cacheKey = [self cacheKeyForIndexPath:indexPathToDelete];
[_cacheDictionary removeObjectForKey:cacheKey];
[_subviewFrameCacheDict removeObjectForKey:cacheKey];
long sectionOfToDeleteItem = indexPathToDelete.section;
long rowOfToDeleteItem = indexPathToDelete.row;
NSMutableDictionary *tempHeightCacheDict = [NSMutableDictionary new];
NSMutableDictionary *tempFrameCacheDict = [NSMutableDictionary new];
for (NSString *key in _cacheDictionary.allKeys) {
NSArray *res = [key componentsSeparatedByString:@"-"];
long section = [res.firstObject integerValue];
long row = [res.lastObject integerValue];
if (section == sectionOfToDeleteItem && row > rowOfToDeleteItem) {
NSNumber *heightCache = _cacheDictionary[key];
NSArray *frameCache = _subviewFrameCacheDict[key];
NSString *newKey = [NSString stringWithFormat:@"%ld-%ld", section, (row - 1)];
[tempHeightCacheDict setValue:heightCache forKey:newKey];
[tempFrameCacheDict setValue:frameCache forKey:newKey];
[_cacheDictionary removeObjectForKey:key];
[_subviewFrameCacheDict removeObjectForKey:key];
}
}
[_cacheDictionary addEntriesFromDictionary:tempHeightCacheDict];
[_subviewFrameCacheDict addEntriesFromDictionary:tempFrameCacheDict];
}
- (void)insertNewDataAtTheBeginingOfSection:(NSInteger)section newDataCount:(NSInteger)count
{
NSMutableDictionary *tempHeightCacheDict = [NSMutableDictionary new];
NSMutableDictionary *tempFrameCacheDict = [NSMutableDictionary new];
for (NSString *key in _cacheDictionary.allKeys) {
NSArray *res = [key componentsSeparatedByString:@"-"];
long originalSection = [res.firstObject integerValue];
long row = [res.lastObject integerValue];
if (originalSection == section) {
NSNumber *heightCache = _cacheDictionary[key];
NSArray *frameCache = _subviewFrameCacheDict[key];
NSString *newKey = [NSString stringWithFormat:@"%ld-%ld", originalSection, (row + count)];
[tempHeightCacheDict setValue:heightCache forKey:newKey];
[tempFrameCacheDict setValue:frameCache forKey:newKey];
[_cacheDictionary removeObjectForKey:key];
[_subviewFrameCacheDict removeObjectForKey:key];
}
}
[_cacheDictionary addEntriesFromDictionary:tempHeightCacheDict];
[_subviewFrameCacheDict addEntriesFromDictionary:tempFrameCacheDict];
}
- (void)insertNewDataAtIndexPaths:(NSArray *)indexPaths
{
NSMutableDictionary *sectionsdict = [NSMutableDictionary new];
for (NSIndexPath *indexPath in indexPaths) {
NSString *sectionkey = [@(indexPath.section) stringValue];
if (![sectionsdict objectForKey:sectionkey]) {
[sectionsdict setValue:[NSMutableArray new] forKey:sectionkey];
}
NSMutableArray *arr = sectionsdict[sectionkey];
[arr addObject:indexPath];
}
for (NSString *sectionkey in sectionsdict.allKeys) {
NSMutableArray *tempHeightCaches = [NSMutableArray new];
NSMutableArray *tempFrameCaches = [NSMutableArray new];
NSInteger section = [sectionkey integerValue];
NSInteger rowCount = [self.modelTableview numberOfRowsInSection:section];
if (rowCount <= 0) {
continue;
} else {
for (int i = 0; i < rowCount; i++) {
[tempHeightCaches addObject:[NSNull null]];
[tempFrameCaches addObject:[NSNull null]];
}
}
for (NSString *key in _cacheDictionary.allKeys) {
NSArray *res = [key componentsSeparatedByString:@"-"];
long originalSection = [res.firstObject integerValue];
long row = [res.lastObject integerValue];
if (originalSection == section) {
NSNumber *heightCache = _cacheDictionary[key];
NSArray *frameCache = _subviewFrameCacheDict[key];
[tempHeightCaches setObject:heightCache atIndexedSubscript:row];
[tempFrameCaches setObject:frameCache atIndexedSubscript:row];
[_cacheDictionary removeObjectForKey:key];
[_subviewFrameCacheDict removeObjectForKey:key];
}
}
NSMutableArray *objsToInsert = [NSMutableArray new];
NSMutableIndexSet *indexSet = [NSMutableIndexSet new];
NSArray *indexPaths = sectionsdict[sectionkey];
[indexPaths enumerateObjectsUsingBlock:^(NSIndexPath *obj, NSUInteger idx, BOOL *stop) {
[objsToInsert addObject:[NSNull null]];
[indexSet addIndex:obj.row];
}];
[tempHeightCaches insertObjects:objsToInsert atIndexes:indexSet];
[tempFrameCaches insertObjects:objsToInsert atIndexes:indexSet];
[tempHeightCaches enumerateObjectsUsingBlock:^(NSNumber *heightCache, NSUInteger idx, BOOL *stop) {
if (![heightCache isKindOfClass:[NSNull class]]) {
NSString *key = [NSString stringWithFormat:@"%zd-%zd", section, idx];
[_cacheDictionary setValue:heightCache forKey:key];
[_subviewFrameCacheDict setValue:[tempFrameCaches objectAtIndex:idx] forKey:key];
}
}];
}
}
- (NSNumber *)heightCacheForIndexPath:(NSIndexPath *)indexPath
{
/*
如果程序卡在了这里很可能是由于你用了“dequeueReusableCellWithIdentifier:forIndexPath:”方法来重用cell换成““dequeueReusableCellWithIdentifier:”不带IndexPath方法即可解决
*/
NSString *cacheKey = [self cacheKeyForIndexPath:indexPath];
return (NSNumber *)[_cacheDictionary objectForKey:cacheKey];
}
- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath
{
NSNumber *cacheHeight = [self heightCacheForIndexPath:indexPath];
if (cacheHeight) {
return [cacheHeight floatValue];
} else {
if (!self.modelCell) {
return 0;
}
if (self.modelTableview && self.modelTableview != self.modelCell.sd_tableView) {
self.modelCell.sd_tableView = self.modelTableview;
}
self.modelCell.sd_indexPath = indexPath;
if (model && keyPath) {
[self.modelCell setValue:model forKey:keyPath];
} else if (self.cellDataSetting) {
self.cellDataSetting(self.modelCell);
}
#ifdef SDDebugWithAssert
/*
如果程序卡在了这里说明你的cell还没有调用“setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin”方法或者你传递的bottomView为nil请检查并修改。例
//注意bottomView不能为nil
[cell setupAutoHeightWithBottomView:bottomView bottomMargin:bottomMargin];
*/
NSAssert(self.modelCell.sd_bottomViewsArray.count, @">>>>>> 你的cell还没有调用“setupAutoHeightWithBottomView:(UIView *)bottomView bottomMargin:(CGFloat)bottomMargin”方法或者你传递的bottomView为nil请检查并修改");
#endif
[self.modelCell.contentView layoutSubviews];
NSString *cacheKey = [self cacheKeyForIndexPath:indexPath];
[_cacheDictionary setObject:@(self.modelCell.autoHeight) forKey:cacheKey];
if (self.modelCell.sd_indexPath && self.modelCell.sd_tableView) {
if (self.modelCell.contentView.shouldReadjustFrameBeforeStoreCache) {
self.modelCell.contentView.height_sd = self.modelCell.autoHeight;
[self.modelCell.contentView layoutSubviews];
}
[self.modelCell.contentView.autoLayoutModelsArray enumerateObjectsUsingBlock:^(SDAutoLayoutModel *model, NSUInteger idx, BOOL *stop) {
[self.modelTableview.cellAutoHeightManager setSubviewFrameCache:model.needsAutoResizeView.frame WithIndexPath:self.modelCell.sd_indexPath];
}];
}
return self.modelCell.autoHeight;
}
}
- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass
{
if (![self.modelCell isKindOfClass:cellClass]) {
self.modelCell = nil;
self.modelCell = [_modelCellsDict objectForKey:NSStringFromClass(cellClass)];
if (!self.modelCell) {
[self registerCellWithCellClass:cellClass];
}
_modelCell.contentView.tag = kSDModelCellTag;
}
if (self.modelCell.contentView.width_sd != self.contentViewWidth) {
_modelCell.contentView.width_sd = self.contentViewWidth;
}
return [self cellHeightForIndexPath:indexPath model:model keyPath:keyPath];
}
- (void)setContentViewWidth:(CGFloat)contentViewWidth
{
if (_contentViewWidth == contentViewWidth) return;
CGFloat lastContentViewWidth = _contentViewWidth;
_contentViewWidth = contentViewWidth;
self.modelCell.contentView.width_sd = self.contentViewWidth;
if (lastContentViewWidth > 0) {
[_subviewFrameCacheDict removeAllObjects];
dispatch_async(dispatch_get_main_queue(), ^{
[self clearHeightCache];
[self.modelTableview reloadData];
});
}
}
- (void)setSubviewFrameCache:(CGRect)rect WithIndexPath:(NSIndexPath *)indexPath
{
if (!self.subviewFrameCacheDict) {
self.subviewFrameCacheDict = [NSMutableDictionary new];
}
NSString *cacheKey = [self cacheKeyForIndexPath:indexPath];
NSMutableArray *caches = [self.subviewFrameCacheDict objectForKey:cacheKey];
if (!caches) {
caches = [NSMutableArray new];
[self.subviewFrameCacheDict setValue:caches forKey:cacheKey];
}
[caches addObject:[NSValue valueWithCGRect:rect]];
}
- (NSMutableArray *)subviewFrameCachesWithIndexPath:(NSIndexPath *)indexPath
{
NSString *cacheKey = [self cacheKeyForIndexPath:indexPath];
return [self.subviewFrameCacheDict valueForKey:cacheKey];
}
@end
@implementation UITableView (SDAutoTableViewCellHeight)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSArray *selStringsArray = @[@"reloadData", @"reloadRowsAtIndexPaths:withRowAnimation:", @"deleteRowsAtIndexPaths:withRowAnimation:", @"insertRowsAtIndexPaths:withRowAnimation:"];
[selStringsArray enumerateObjectsUsingBlock:^(NSString *selString, NSUInteger idx, BOOL *stop) {
NSString *mySelString = [@"sd_" stringByAppendingString:selString];
Method originalMethod = class_getInstanceMethod(self, NSSelectorFromString(selString));
Method myMethod = class_getInstanceMethod(self, NSSelectorFromString(mySelString));
method_exchangeImplementations(originalMethod, myMethod);
}];
});
}
- (void)sd_reloadData
{
if (!self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData) {
[self.cellAutoHeightManager clearHeightCache];
}
[self sd_reloadData];
self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = NO;
}
- (void)sd_reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[self.cellAutoHeightManager clearHeightCacheOfIndexPaths:indexPaths];
[self sd_reloadRowsAtIndexPaths:indexPaths withRowAnimation:animation];
}
- (void)sd_deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
for (NSIndexPath *indexPath in indexPaths) {
[self.cellAutoHeightManager deleteThenResetHeightCache:indexPath];
}
[self sd_deleteRowsAtIndexPaths:indexPaths withRowAnimation:animation];
}
- (void)sd_insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
{
[self.cellAutoHeightManager insertNewDataAtIndexPaths:indexPaths];
[self sd_insertRowsAtIndexPaths:indexPaths withRowAnimation:animation];
}
/*
* 下一步即将实现的功能
- (void)sd_moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
{
[self sd_moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
}
*/
- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath model:(id)model keyPath:(NSString *)keyPath cellClass:(Class)cellClass contentViewWidth:(CGFloat)contentViewWidth
{
self.cellAutoHeightManager.modelTableview = self;
self.cellAutoHeightManager.contentViewWidth = contentViewWidth;
return [self.cellAutoHeightManager cellHeightForIndexPath:indexPath model:model keyPath:keyPath cellClass:cellClass];
}
- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellClass:(__unsafe_unretained Class)cellClass cellContentViewWidth:(CGFloat)width cellDataSetting:(AutoCellHeightDataSettingBlock)cellDataSetting
{
self.cellDataSetting = cellDataSetting;
return [self cellHeightForIndexPath:indexPath model:nil keyPath:nil cellClass:cellClass contentViewWidth:width];
}
- (void)reloadDataWithExistedHeightCache
{
self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = YES;
[self reloadData];
}
- (void)reloadDataWithInsertingDataAtTheBeginingOfSection:(NSInteger)section newDataCount:(NSInteger)count
{
self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = YES;
[self.cellAutoHeightManager insertNewDataAtTheBeginingOfSection:section newDataCount:count];
[self reloadData];
}
- (void)reloadDataWithInsertingDataAtTheBeginingOfSections:(NSArray *)sectionNumsArray newDataCounts:(NSArray *)dataCountsArray
{
self.cellAutoHeightManager.shouldKeepHeightCacheWhenReloadingData = YES;
[sectionNumsArray enumerateObjectsUsingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
int section = [num intValue];
int dataCountForSection = [dataCountsArray[idx] intValue];
[self.cellAutoHeightManager insertNewDataAtTheBeginingOfSection:section newDataCount:dataCountForSection];
}];
[self reloadData];
}
- (CGFloat)cellsTotalHeight
{
CGFloat h = 0;
if (!self.cellAutoHeightManager.heightCacheDict.count) {
[self reloadData];
}
NSArray *values = [self.cellAutoHeightManager.heightCacheDict allValues];
for (NSNumber *number in values) {
h += [number floatValue];
}
return h;
}
- (SDCellAutoHeightManager *)cellAutoHeightManager
{
SDCellAutoHeightManager *cellAutoHeightManager = objc_getAssociatedObject(self, _cmd);
if (!cellAutoHeightManager) {
cellAutoHeightManager = [[SDCellAutoHeightManager alloc] init];
[self setCellAutoHeightManager:cellAutoHeightManager];
}
return cellAutoHeightManager;
}
- (void)setCellAutoHeightManager:(SDCellAutoHeightManager *)cellAutoHeightManager
{
objc_setAssociatedObject(self, @selector(cellAutoHeightManager), cellAutoHeightManager, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setCellDataSetting:(AutoCellHeightDataSettingBlock)cellDataSetting
{
self.cellAutoHeightManager.cellDataSetting = cellDataSetting;
}
- (AutoCellHeightDataSettingBlock)cellDataSetting
{
return self.cellAutoHeightManager.cellDataSetting;
}
@end
@implementation UITableViewController (SDTableViewControllerAutoCellHeight)
- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellContentViewWidth:(CGFloat)width
{
return [self cellHeightForIndexPath:indexPath cellContentViewWidth:width tableView:self.tableView];
}
@end
@implementation NSObject (SDAnyObjectAutoCellHeight)
- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath cellContentViewWidth:(CGFloat)width tableView:(UITableView *)tableView
{
tableView.cellAutoHeightManager.modelTableview = tableView;
if (tableView.cellAutoHeightManager.contentViewWidth != width) {
tableView.cellAutoHeightManager.contentViewWidth = width;
}
if ([tableView.cellAutoHeightManager heightCacheForIndexPath:indexPath]) {
return [[tableView.cellAutoHeightManager heightCacheForIndexPath:indexPath] floatValue];
}
UITableViewCell *cell = [tableView.dataSource tableView:tableView cellForRowAtIndexPath:indexPath];
tableView.cellAutoHeightManager.modelCell = cell;
if (cell.contentView.width_sd != width) {
cell.contentView.width_sd = width;
}
return [[tableView cellAutoHeightManager] cellHeightForIndexPath:indexPath model:nil keyPath:nil];
}
@end