前言
在学习别人文章的时候做一个记录,免得在需要的时候找不到。本文主要记录原理性的内容,也会包含一些代码,具体使用请看文章。先把文章地址贴出来
AsyncDisplayKit2.0教程上
AsyncDisplayKit2.0教程下
这篇文章中的例子可以直接运行,另外的可以自己查看 AsyncDisplaykit2.0
安装
项目使用CocoaPods安装AsynDisplayKit,在Pod文件中输入安装
source 'https://github.com/CocoaPods/Specs.git'
target "AsyncDisplayKitForMit" do
pod 'AsyncDisplayKit'
end
使用介绍
在主线程中进行的一些大量操作包括:
计算大小和布局:包括 -heightForRowAtIndexPath: 或对 UILabel 上进行 -sizeThatFits 调用,以及大量的自动布局约束的解析。
图片解码:在一个 Image View 上使用 UIImage 就意味着要进行图片数据的解码。
绘图:复杂文本以及手动绘制渐变色和阴影。
对象生命周期:创建、操作和销毁系统对象(比如创建一个 UIView)。
在正确使用的情况下,AsynDisplayKit 默认允许你以异步方式操作所有的计算大小、布局和绘图操作。不需要进行任何其它的优化,App 就能够大量减少需要在主线程上进行的工作。
ASDisplayNode 简介
ASDisplayNode 是 ASDK 的核心类,甚至可以说是“心脏”,就像 MVC 中的 view,可以看做是另一种 UIView 或 CALayer。理解一个“节点”对象的最好方法是参考 UIView 和 CALayer 的关系,你对此应该很熟悉了。
在一个 iOS App 中,屏幕上的每一个对象都表示了一个 CALayer 对象。UIView 私底下会创建和拥有一个 CALayer,通过这个 CALayer 来感知触摸或其他功能。UIView 自身并不是 CALayer 子类。相反,它包含了一个 CALayer 对象,并为它添加了一些功能。
这种概念也沿袭进了 ASDisplayNode:你可以认为它包含了一个 view,就好比 view 包含了一个 layer。
将节点通过一个普通的 View 放到表格上,最终使它们能够从后台队列中创建和配置,默认情况下,它们会被异步渲染。
幸运的是,这个 API 处理节点的方式和使用 UIView 或 CALayer 差不多。所有的 View 属性都可以在节点类上找到相同的属性。你还可访问底层的 view 或 layer——就像你可以通过 .layer 访问 UIView 的 layer 一样。
节点容器
要让节点对象尽可能地提升性,必须将它和 4 个容器类协同工作。
这 4 个容器类分别是:
ASViewController: 一个 UIViewController 子类,允许你创建节点并进行管理。
ASCollectionNode 和 ASTableNode: 对应于 UICollectionView 和 UITableView 的 Node 子类,封装了其底层细节。
ASPagerNode: 一个 ASCollectionNode 子类,封装了扫动手势,对应于 UIKit 的 UIPageViewController。
这也太简单了,但真正的秘密其实来自于 ASRangeController,这 4 个类都会通过它来影响所包含的节点的行为。现在,请听我说,暂且将那些内容保留到后面解释。
使用
a:导入框架 #import
b:声明tabelNode属性
@property (strong, nonatomic) ASTableNode *tableNode;
c:初始化容器,设置 TableNode 的数据和委托
和 UITableView 一样,ASTableNode 也使用了数据源和委托来获得相关信息。Table Node 的 ASTableDataSource 和 ASTableDelegate 协议和 UITableViewDataSource 和 UITableViewDelegate 协议非常类似。事实上,它们的方法定义都很像,比如 -tableNode:numberOfRowsInSection:。当然,这两套协议也不是完全没有区别,因为 ASTableNode 的行为和 UITableView 多少还是有一点不同。
_tableNode = [[ASTableNode alloc]initWithStyle:UITableViewStylePlain];
self.tableNode.dataSource = self;
self.tableNode.delegate = self;
self.tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.view addSubnode:self.tableNode];
d:然后,修改 -viewWillLayoutSubviews 方法:
- (void)viewWillLayoutSubviews {
[superviewWillLayoutSubviews];
self.tableNode.frame = self.view.bounds;
}
到现在相当于创建好了tableview并设置好其frame,显示cell需要再实现其数据源方法
e:遵守数据源相关协议
@interface AnimalTableController()
f:实现两个显示前重要的协议
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section { //返回多少行
return self.animals.count;
}
然后,ASTableNodes 返回 cell 的方式和 UITableView 有所不同。将 -tableView:cellForRowAtIndexPath: 方法替换为:
(ASCellNodeBlock)tableNode:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath*)indexPath {
RainforestCardInfo *animal = self.animals[indexPath.row];
ASCellNode *(^cellNodeBlock)() = ^ASCellNode *() {
TableViewNodeCell *cellNode = [[TableViewNodeCell alloc] initWithHaiTao:haiTaoStyle];
return cellNode;
};
return cellNodeBlock;
}
代码解释如下:
TableViewNodeCell继承自ASCellNode,一个 ASCellNode 等同于 UITableViewCell 或 UICollectionViewCell。尤其要注意这个方法返回的是一个 ASCellNodeBlock。因为一个 ASTableNode 在内部维护了所有的 cell,为每个 cell 的 Index Path 指定了一个块,这样当需要的时候就能够异步地初始化所有 cell。
首先,你需要获得一个数据模型对象,以便渲染这个 cell。这已经是固定的套路了。获取数据,然后将它传给后面的闭包。IndexPath 在闭包中不需要,这个例子中在闭包被调用之前数据就会先发生改变。
然后返回一个闭包,闭包中返回的类型必须是 ASCellNode。
不需要关心 cell 重用的问题,因此只需要实例化一个 cell。注意,你返回的是一个 CardNode 而不是 CardCell。
这里有一点要注意。你可能也注意到了,使用 ASDK 的时候 cell 不会进行重用。
g:相信你也知道了,在使用 UITableView 的时候通常都需要实现一个 -tableView:heightForRowAtIndexPath: 方法。这是因为 UIKit 是通过这个委托方法来计算每个 cell 的高度的。
ASTableDelegate 中没有 -tableView:heightForRowAtIndexPath: 方法。如果使用 ASDK, 所有的 ASCellNodes 都自己负责计算它们的大小。不需要提供一个固定的高度,你可以为每个 cell 指定一个最大尺寸和最小尺寸。这个例子中,你需要让每个 cell 至少占据屏幕 2/3 的高度。
现在我们暂时不讨论这个,细节会在第二部分进行讨论。
现在,将 -tableView:heightForRowAtIndexPath: 方法替换为:
- (ASSizeRange)tableView:(ASTableView*)tableNode constrainedSizeForRowAtIndexPath:(NSIndexPath*)indexPath {
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGSizemin= CGSizeMake(width, ([UIScreen mainScreen].bounds.size.height/3) *2); CGSizemax= CGSizeMake(width, INFINITY);
returnASSizeRangeMake(min,max);
}
//剩下的重要内容就在自定义ASCellNode中了,包括里面的控件创建与布局。