本文转载至 http://www.tuicool.com/articles/I7ji2uM

Basic Information

  • Name : UITableView-FDTemplateLayoutCell
  • Site : https://github.com/forkingdog/UITableView-FDTemplateLayoutCell
  • Repo : https://github.com/forkingdog/UITableView-FDTemplateLayoutCell
  • Revision : e3ee86ce419d18d3ff735056f1474f2863e43003
  • Description : 简单易用的UITableViewCell自动高度。 作者的博客文章 http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/

Global Note

简单易用,但在一些复杂界面(例如聊天窗口)中使用时还是需要考虑更多优化问题。

File Notes

0. UITableView+FDTemplateLayoutCell.h

  • Path : /Classes/UITableView+FDTemplateLayoutCell.h
  • Line : 35 - 35
  • Note :
- (__kindof UITableViewCell *)fd_templateCellForReuseIdentifier:(NSString *)identifier;

__kindof XXXClass 可以这么用

1. UITableView+FDTemplateLayoutCell.h

  • Path : /Classes/UITableView+FDTemplateLayoutCell.h
  • Line : 28 - 28
  • Note :
@interface UITableView (FDTemplateLayoutCell)

UITableView的extension

2. UITableView+FDTemplateLayoutCell.h

  • Path : /Classes/UITableView+FDTemplateLayoutCell.h
  • Line : 87 - 99
  • Note :
@interface UITableViewCell (FDTemplateLayoutCell)

/// Indicate this is a template layout cell for calculation only.
/// You may need this when there are non-UI side effects when configure a cell.
/// Like:
/// - (void)configureCell:(FooCell *)cell atIndexPath:(NSIndexPath *)indexPath {
/// cell.entity = [self entityAtIndexPath:indexPath];
/// if (!cell.fd_isTemplateLayoutCell) {
/// [self notifySomething]; // non-UI side effects
/// }
/// }
///
@property (nonatomic, assign) BOOL fd_isTemplateLayoutCell;

使用 UITableViewCell 模板Cell计算高度,通过 fd_isTemplateLayoutCell 可在Cell内部判断当前是否是模板Cell。可以省去一些与高度无关的操作。

3. UITableView+FDTemplateLayoutCell.m

  • Path : /Classes/UITableView+FDTemplateLayoutCell.m
  • Line : 221 - 229
  • Note :
@implementation UITableViewCell (FDTemplateLayoutCell)

- (BOOL)fd_isTemplateLayoutCell {
return [objc_getAssociatedObject(self, _cmd) boolValue];
} - (void)setFd_isTemplateLayoutCell:(BOOL)isTemplateLayoutCell {
objc_setAssociatedObject(self, @selector(fd_isTemplateLayoutCell), @(isTemplateLayoutCell), OBJC_ASSOCIATION_RETAIN);
}

使用runtime增加属性的实现。

SEL类型的_cmd , 每个方法内部都有,表示方法自身。 因此,可以NSStringFromSelector(_cmd)返回当前方法名称。

使用 get的SEL(也就是_cmd)作为objc_getAssociatedObject的key。值得学习。但要注意set中也要用相同的key,也就是@selector(fd_isTemplateLayoutCell)。

4. UITableView+FDTemplateLayoutCell.m

  • Path : /Classes/UITableView+FDTemplateLayoutCell.m
  • Line : 36 - 43
  • Note :
        static const CGFloat systemAccessoryWidths[] = {
[UITableViewCellAccessoryNone] = 0,
[UITableViewCellAccessoryDisclosureIndicator] = 34,
[UITableViewCellAccessoryDetailDisclosureButton] = 68,
[UITableViewCellAccessoryCheckmark] = 40,
[UITableViewCellAccessoryDetailButton] = 48
};
contentViewWidth -= systemAccessoryWidths[cell.accessoryType];

指定索引定义数组的方式。oc的小技巧真不少。

5. UITableView+FDTemplateLayoutCell.m

  • Path : /Classes/UITableView+FDTemplateLayoutCell.m
  • Line : 57 - 64
  • Note :
        // Add a hard width constraint to make dynamic content views (like labels) expand vertically instead
// of growing horizontally, in a flow-layout manner.
NSLayoutConstraint *widthFenceConstraint = [NSLayoutConstraint constraintWithItem:cell.contentView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:contentViewWidth];
[cell.contentView addConstraint:widthFenceConstraint]; // Auto layout engine does its math
fittingHeight = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
[cell.contentView removeConstraint:widthFenceConstraint];

AutoLayout 计算Size的方法 systemLayoutSizeFittingSize。 这里新增一个宽度的约束,计算高度后再移除掉。 不错的想法。

6. UITableView+FDTemplateLayoutCell.m

  • Path : /Classes/UITableView+FDTemplateLayoutCell.m
  • Line : 79 - 81
  • Note :
        // Try '- sizeThatFits:' for frame layout.
// Note: fitting height should not include separator view.
fittingHeight = [cell sizeThatFits:CGSizeMake(contentViewWidth, 0)].height;

不使用AutoLayout的情况下,使用sizeThatFits来获取大小。自定义cell需要实现这个函数。

7. UITableView+FDTemplateLayoutCell.m

  • Path : /Classes/UITableView+FDTemplateLayoutCell.m
  • Line : 100 - 121
  • Note :
- (__kindof UITableViewCell *)fd_templateCellForReuseIdentifier:(NSString *)identifier {
NSAssert(identifier.length > 0, @"Expect a valid identifier - %@", identifier); NSMutableDictionary<NSString *, UITableViewCell *> *templateCellsByIdentifiers = objc_getAssociatedObject(self, _cmd);
if (!templateCellsByIdentifiers) {
templateCellsByIdentifiers = @{}.mutableCopy;
objc_setAssociatedObject(self, _cmd, templateCellsByIdentifiers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} UITableViewCell *templateCell = templateCellsByIdentifiers[identifier]; if (!templateCell) {
templateCell = [self dequeueReusableCellWithIdentifier:identifier];
NSAssert(templateCell != nil, @"Cell must be registered to table view for identifier - %@", identifier);
templateCell.fd_isTemplateLayoutCell = YES;
templateCell.contentView.translatesAutoresizingMaskIntoConstraints = NO;
templateCellsByIdentifiers[identifier] = templateCell;
[self fd_debugLog:[NSString stringWithFormat:@"layout cell created - %@", identifier]];
} return templateCell;
}

关键的函数。

  • 给当前TableView关联一个可变字典。
  • 每一种 CellIdentifier 一个Key。
  • 用于存储模板Cell。
  • 模板Cell用于在内存中构建Cell,并对这个模板Cell计算高度。

8. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 26 - 26
  • Note :
typedef NSMutableArray<NSMutableArray<NSNumber *> *> FDIndexPathHeightsBySection;

缓存高度。每个section一个数组。

9. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 29 - 30
  • Note :
@property (nonatomic, strong) FDIndexPathHeightsBySection *heightsBySectionForPortrait;
@property (nonatomic, strong) FDIndexPathHeightsBySection *heightsBySectionForLandscape;

横屏竖屏各自缓存。

10. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 45 - 45
  • Note :
    return UIDeviceOrientationIsPortrait([UIDevice currentDevice].orientation) ? self.heightsBySectionForPortrait: self.heightsBySectionForLandscape;

判断横竖屏

11. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 65 - 73
  • Note :
- (CGFloat)heightForIndexPath:(NSIndexPath *)indexPath {
[self buildCachesAtIndexPathsIfNeeded:@[indexPath]];
NSNumber *number = self.heightsBySectionForCurrentOrientation[indexPath.section][indexPath.row];
#if CGFLOAT_IS_DOUBLE
return number.doubleValue;
#else
return number.floatValue;
#endif
}

CGFLOAT_IS_DOUBLE 注意这个。

12. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 124 - 124
  • Note :
        [self methodSignatureForSelector:nil];

这个没看懂啊 NSMethodSignature

13. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 133 - 145
  • Note :
// We just forward primary call, in crash report, top most method in stack maybe FD's,
// but it's really not our bug, you should check whether your table view's data source and
// displaying cells are not matched when reloading.
static void __FD_TEMPLATE_LAYOUT_CELL_PRIMARY_CALL_IF_CRASH_NOT_OUR_BUG__(void (^callout)(void)) {
callout();
}
#define FDPrimaryCall(...) do {__FD_TEMPLATE_LAYOUT_CELL_PRIMARY_CALL_IF_CRASH_NOT_OUR_BUG__(^{__VA_ARGS__});} while(0) @implementation UITableView (FDIndexPathHeightCacheInvalidation) - (void)fd_reloadDataWithoutInvalidateIndexPathHeightCache {
FDPrimaryCall([self fd_reloadData];);
}

一个奇技淫巧。看来这样可以在栈回朔中显示出这个“提示用的”方法名称。

14. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 147 - 168
  • Note :
+ (void)load {
// All methods that trigger height cache's invalidation
SEL selectors[] = {
@selector(reloadData),
@selector(insertSections:withRowAnimation:),
@selector(deleteSections:withRowAnimation:),
@selector(reloadSections:withRowAnimation:),
@selector(moveSection:toSection:),
@selector(insertRowsAtIndexPaths:withRowAnimation:),
@selector(deleteRowsAtIndexPaths:withRowAnimation:),
@selector(reloadRowsAtIndexPaths:withRowAnimation:),
@selector(moveRowAtIndexPath:toIndexPath:)
}; for (NSUInteger index = 0; index < sizeof(selectors) / sizeof(SEL); ++index) {
SEL originalSelector = selectors[index];
SEL swizzledSelector = NSSelectorFromString([@"fd_" stringByAppendingString:NSStringFromSelector(originalSelector)]);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}

神奇的load方法。当第一次看到load方法,惊呼Objective C真是太灵活了。

load 文档参考如下:

Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.
The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.
The order of initialization is as follows:
All initializers in any framework you link to.
All +load methods in your image.
All C++ static initializers and C/C++ __attribute__(constructor) functions in your image.
All initializers in frameworks that link to you.
In addition:
A class’s +load method is called after all of its superclasses’ +load methods.
A category +load method is called after the class’s own +load method.
In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes may not have run yet.

15. UITableView+FDIndexPathHeightCache.m

  • Path : /Classes/UITableView+FDIndexPathHeightCache.m
  • Line : 162 - 166
  • Note :
        SEL originalSelector = selectors[index];
SEL swizzledSelector = NSSelectorFromString([@"fd_" stringByAppendingString:NSStringFromSelector(originalSelector)]);
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);

method_exchangeImplementations 可以交换两个Method。

类似做Windows开发时的API HOOK(detours、mhook),这Objective C的Runtime都给提供好了,更上层一些。

俗称“swizzle method”。

16. UITableView+FDKeyedHeightCache.m

  • Path : /Classes/UITableView+FDKeyedHeightCache.m
  • Line : 75 - 86
  • Note :
@implementation UITableView (FDKeyedHeightCache)

- (FDKeyedHeightCache *)fd_keyedHeightCache {
FDKeyedHeightCache *cache = objc_getAssociatedObject(self, _cmd);
if (!cache) {
cache = [FDKeyedHeightCache new];
objc_setAssociatedObject(self, _cmd, cache, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return cache;
} @end

每个TableView关联一个高度缓存

17. UITableView+FDTemplateLayoutCellDebug.h

  • Path : /Classes/UITableView+FDTemplateLayoutCellDebug.h
  • Line : 25 - 25
  • Note :
@interface UITableView (FDTemplateLayoutCellDebug)

附加功能推荐用Category这种方式增加。

Summarize

UITableView-FDTemplateLayoutCell 学习笔记的更多相关文章

  1. UITableView 学习笔记

    http://www.cnblogs.com/smileEvday/archive/2012/06/28/tableView.html UITableView学习笔记 作者:一片枫叶 看TableVi ...

  2. iOS学习笔记之UITableViewController&UITableView

    iOS学习笔记之UITableViewController&UITableView 写在前面 上个月末到现在一直都在忙实验室的事情,与导师讨论之后,发现目前在实验室完成的工作还不足以写成毕业论 ...

  3. iOS学习笔记(4) — UITableView的 重用机制

    iOS学习笔记(4) — UITableView的 重用机制 UITableView中的cell是动态的,在使用过程中,系统会根据屏幕的高度(480)和每个cell的高度计算屏幕中需要显示的cell的 ...

  4. 转:UITableView学习笔记

    UITableView学习笔记        作者:一片枫叶 看TableView的资料其实已经蛮久了,一直想写点儿东西,却总是因为各种原因拖延,今天晚上有时间静下心来记录一些最近学习的 TableV ...

  5. 离屏渲染学习笔记 /iOS圆角性能问题

    离屏渲染学习笔记 一.概念理解 OpenGL中,GPU屏幕渲染有以下两种方式: On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行. O ...

  6. [置顶] iOS学习笔记47——图片异步加载之EGOImageLoading

    上次在<iOS学习笔记46——图片异步加载之SDWebImage>中介绍过一个开源的图片异步加载库,今天来介绍另外一个功能类似的EGOImageLoading,看名字知道,之前的一篇学习笔 ...

  7. IOS学习笔记48--一些常见的IOS知识点+面试题

      IOS学习笔记48--一些常见的IOS知识点+面试题   1.堆和栈什么区别? 答:管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制:对于堆来说,释放工作由程序员控制,容易产生memor ...

  8. iOS学习笔记-自己动手写RESideMenu

    代码地址如下:http://www.demodashi.com/demo/11683.html 很多app都实现了类似RESideMenu的效果,RESideMenu是Github上面一个stars数 ...

  9. iOS学习笔记20-地图(二)MapKit框架

    一.地图开发介绍 从iOS6.0开始地图数据不再由谷歌驱动,而是改用自家地图,当然在国内它的数据是由高德地图提供的. 在iOS中进行地图开发主要有三种方式: 利用MapKit框架进行地图开发,利用这种 ...

随机推荐

  1. (笔记)ubuntu中取消文件夹或文件等右下解一把锁的标志的方法

    ubuntu中取消文件夹或文件等右下解一把锁的标志的方法   方法:   sudo chmod -R 777 路径(文件夹或文件)   对文件递归做改变权限为可读可写可运行,即可.

  2. 使用Photoshop实现雪花飘落的效果

    一.准备工作 软件环境:PhotoshopCS5 实验目的:雪花飘落的效果 二.实验步骤 1,打开素材图片并将原图层复制 2,在菜单栏内选择:滤镜->像素化->点状化,单元格大小选6  提 ...

  3. SpringBoot系列八:SpringBoot整合消息服务(SpringBoot 整合 ActiveMQ、SpringBoot 整合 RabbitMQ、SpringBoot 整合 Kafka)

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合消息服务 2.具体内容 对于异步消息组件在实际的应用之中会有两类: · JMS:代表作就是 ...

  4. 目标检测之dpm---hog的最优升级版

    http://blog.csdn.net/ttransposition/article/details/12966521 http://blog.csdn.net/carson2005/article ...

  5. Linux Shell的 & 、&& 、 ||

    Linux Shell的 & .&& . || 收藏 hanzhankang 发表于 3年前 阅读 18472 收藏 20 点赞 4 评论 0 开程序员的淘宝店!寻找开源技术服 ...

  6. C#.NET MVC 枚举转dictionary自动装载生成下拉框

      /// <summary> /// 枚举转SelectListItem /// </summary> public class Enum_Helper { /// < ...

  7. Maven初步踩坑

    2015-02-08 今天创建maven项目,要从中央仓库下载一堆包到本地仓库,等了好久.结果下好了之后,maven项目上有个感叹号,也没有发现代码里哪配置有错误. 和实验室好多小伙伴一起交流 也没找 ...

  8. Android检查设备是否可以访问互联网,判断Internet连接,测试网络请求,解析域名

    安卓SDK提供了ConnectivityManager类,那么我们就可以轻松的获取设备的网络状态以及联网方式等信息. 但是要想知道安卓设备连接的网络能不能访问到Internet,就要费一番周折了. 本 ...

  9. [WPF打印]WPF 文档元素(Run TextBlock Paragraph)的文字对齐方式

    最近开发WPF程序,需要打印,用到了FlowDocument(这相当于有了打印模版,而且可以随时修改,真的是挺方便的).可是在输出表格形数据(这种情况恐怕是大多数~)时遇到了点儿麻烦. 由于Table ...

  10. Java进阶面试题列表

    面向对象编程的基本理念与核心设计思想 解释下多态性(polymorphism),封装性(encapsulation),内聚(cohesion)以及耦合(coupling). 继承(Inheritanc ...