标签(空格分隔): UITableCell iOS
最近看到一个开源库用来动态计算UITableCell高度的,感觉挺不错,学习下它的实现方式。
UITableView-FDTemplateLayoutCell
这个库的使用很简单,官方文档已经说明了,主要支持两种方式的使用:
- 简单使用,没有缓存计算过的cell高度
#import "UITableView+FDTemplateLayoutCell.h"
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [tableView fd_heightForCellWithIdentifier:@"reuse identifer" configuration:^(id cell) {
// Configure this cell with data, same as what you've done in "-tableView:cellForRowAtIndexPath:"
// Like:
// cell.entity = self.feedEntities[indexPath.row];
}];
}
- 高级使用,支持缓存已经计算过的Cell高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return [tableView fd_heightForCellWithIdentifier:@"identifer" cacheByIndexPath:indexPath configuration:^(id cell) {
// configurations
}];
}
并且不用担心重复使用cell造成高度缓存错误问题,因为在实现代码里,作者利用OC的运行时 Method Swizzling机制替换了原有的方法,这里会存在一个问题就是如果使用这个库,修改了Private method不知道Apo Store审核会通过不。
UISectionRowData refreshWithSection:tableView:tableViewRowData
这个是私有的当reload Data 或者相关的方法被调用时,iOS内部调用的方法。该作者替换了这个方法,使得调用这个方法之前,会先清空cached 的高度信息,因此才不用担心缓存高度的问题。具体实现过程如下:
__attribute__((constructor)) static void FDTemplateLayoutCellHeightCacheInvalidationEntryPoint()
{
// Swizzle a private method in a private class "UISectionRowData", we try to assemble this
// selector instead of using the whole literal string, which may be more safer when submit
// to App Store.
NSString *privateSelectorString = [@"refreshWithSection:" stringByAppendingString:@"tableView:tableViewRowData:"];
SEL originalSelector = NSSelectorFromString(privateSelectorString);
Method originalMethod = class_getInstanceMethod(NSClassFromString(@"UISectionRowData"), originalSelector); // 获取原始实例方法
if (!originalMethod) {
return;
}
void (*originalIMP)(id, SEL, NSUInteger, id, id) = (typeof(originalIMP))method_getImplementation(originalMethod); //通过原始method获得它的实现
void (^swizzledBlock)(id, NSUInteger, id, id) = ^(id self, NSUInteger section, UITableView *tableView, id rowData) {
// Invalidate height caches first
[tableView fd_invalidateHeightCaches];
// Call original implementation
originalIMP(self, originalSelector, section, tableView, rowData);
};
method_setImplementation(originalMethod, imp_implementationWithBlock(swizzledBlock));
}
里面的attribute((section(“name”)) 是GCC提供的一种扩展,可以自定义段,这里的这个方法调用会在main()函数之前,以及所有classes 被映射到runtime中才执行。
这个扩展方法实现主要用到了如下两个方法:
[NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:CGRectGetWidth(self.frame)];
[cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]
这个方法的使用用来计算适配layout之后,cell的高度,这样即可动态得到cell的高度。确实很不错
其他讨论cell的文章:
iOS 8 Tutorial Series: Auto Sizing Table Cells
Auto Layout for Table View Cells with Dynamic Heights