Swift学徒

AsyncDisplayKit 教程(5):智能预加载

预加载的使用

你可能曾经写过这样的 App :根据 ScrollView /PageView 的滚动来提前加载数据。比如一个全屏的图片浏览器,总是提前加载下几张图片,这样你的用户基本不会看到空白图片。 如果你想写这样一个程序,你会发现需要考虑的东西太多了。

之前我们说, ASRangeController 会魔法,现在我们解释一下。

在每个容器类中,每个 node 都会有一个“界面状态”的概念。在任何给定的时刻,一个 node 都会处于下列状态:

  • Preload Range: 这个位置距离“可见”最远。这时 cell 中的所有 subnode 的内容,比如 ASNetworkImageNode 需要从外部数据源、API 或本地缓存中下载内容。以 batch fetching 为例,这个时候应当抓取模型对象。
  • Display Range: 处于这个位置的时候,将进行显示操作,比如绘制文本和图片解码。
  • Visible Range: 处于这个位置的时候,node 至少在屏幕上有一个像素。

这些 Range 都能以“整屏”的方式度量,还可以轻易地通过 ASRangeTuningParameters 属性进行调整。

例如,如果你用一个 ASNetworkImageNode 来显示一页图片。当每一页进入 Preload Range 位置时,都要从网络请求数据,而当它进入 Display Range 位置时又会对已经下载的图片进行解码。

通常,如果你不愿意,你可以不考虑这些 Range。它们已经内置了,比如 ASNetworkImageNode 和 ASTextNode。

Node 界面状态 Callbacks

每个 node 都有一个 interfaceState 属性,它是一个 “bitfield” (NS_OPTION) 类型 ASInterfaceState。因为 ASCellNode 在 Scroll View 中滚动时,Scroll View 是受 ASRangeController 管理的,每个subnode 都会适时改变它的 interfaceState 属性。这样,即使层次最深的那个 node 也可以根据 interfaceState 的改变做出反应。

幸运的是,几乎不需要直接操纵 node 的 interfaceState 属性。通常,需要对节点的状态改变进行响应即可。也就是所谓的 界面状态 Callbacks。

对 node 进行命名

为了明白节点的状态是怎样变化的,我们需要给它命名。这样,你就可以看到节点是什么时候加载数据,显示内容,进入屏幕,以及它离开时的逆过程。

找到 tableNode:nodeBlockForRowAtIndexPath: 方法中的这个注释:

1
//You'll add something extra here later...

在注释的下面,加入下句,给每个 cell 一个 debugName 值。

1
cardNode.debugName = [NSString stringWithFormat:@"cell %zd", indexPath.row];

现在可以跟踪到每个 cell 进入不同范围的变化过程。

观察 Cell

打开 CardNode_InterfaceCallbacks.m。这里有 6 个方法,这些方法用来打印节点进入各种范围的过程。取消注释,运行程序,慢慢滚动界面。同时,观察 cell 状态的改变。

注意:大部分情况下,你只需要用到两个关于 ASInterfaceState 的改变方法:didEnterVisibleStatedidExitVisibleState。也就是说,大量底层的工作已经为你完成了。要想知道什么时候需要用到 Preload 和 Display 状态,请看 ASNetworkImageNode 的源代码。所有的 Network Image Node 都会自动抓取和解码,以及释放内存。


万恶胖为首 wechat
关注公众号(ID:SwiftBetter),进一步探讨交流。