【翻译】Texture 核心概念--子类(Subclassing)

创建子类时最重要的区别是,你是在编写 ASViewController,还是 ASDisplayNode。这听起来很明显,但因为其中有一些微妙的差异,在头脑里记住这个概念还是很重要的。

July 4, 2017 -
ios texture 翻译

本文翻译自 TextureSubclassing 一文。

子类


创建子类时最重要的区别是,你是在编写 ASViewController,还是 ASDisplayNode。这听起来很明显,但因为其中有一些微妙的差异,在头脑里记住这个概念还是很重要的。

ASDisplayNode


而子类化 node 类似于编写 UIView 的子类,需要遵循几条准则来确保你充分利用了框架的潜力,并让你的 node 的行为符合预期。

-init

这个方法在使用 nodeBlocks 时是在后台线程上调用。但是,因为直到 -init 运行完成之前都没有其它方法可以运行,所以这个方法里永远不必有锁。

最重要的是要记住,你的 init 方法必须能被任何队列调用。最明显的是,这意味着你不应该初始化任何 UIKit 对象,也不应在 node 里(例如 node.layer.Xnode.view.X)触碰 view 或 layer,或在你的初始化里添加任何手势。作为替代,在 -didLoad 里做这些事情。

-didLoad

这个方法在概念上类似于 UIViewController 的 -viewDidLoad 方法,并且是背景视图被加载的点。它保证在主线程上被调用,并且适合做任何 UIKit 相关的事(例如添加手势识别,触碰 view 或 layer,初始化 UIKit 对象)。

-layoutSpecThatFits:

这个方法确定布局和在后台线程做繁重的计算。这个方法是你创建布局规格对象的地方,它将产生 node 的大小,以及所有子 node 的大小和位置。这是你将放置大部分布局代码的地方。

你创建的布局规格对象,在这个点返回之前,都是可调整的。在这个点之后,它将是不可变的。重点是记住不要缓存布局规格供以后使用,而是改为在必要时重建它们。

因为它在后台线程上运行,你不应该在此设置任何 node.viewnode.layer 属性。还有,除非你知道你在做什么,否则不要在这个方法里创建任何 node。另外,与其它重写方法不同,没有必要通过调用 super 来开始这个方法。

-layout

在这个方法里对 super 的调用是应用 layoutSpec 的结果的地方;在这个方法里调用 super 之后,布局规格将被计算,并且全部子 node 将被测量和定位。

-layout 在概念上类似于 UIViewController 的 -viewWillLayoutSubviews。如果需要,这是一个很好的点去改变 hidden 的属性,设置 view 的基本属性(非可布局属性)或设置背景颜色。你可以在 -layoutSpecThatFits: 里放置背景颜色,但可能会有时间问题。如果你恰巧在使用任何 UIView,你可以在这里设置它们的框架。然而,你可以随时使用 -initWithViewBlock: 创建一个 node 包装器,然后在其它地方的后台线程里调整它的大小。

这个方法在主线程上被调用。然而,如果你在使用布局规格,你不应该太依赖这个方法,因为最好不要在主线程上布局。不到 1/10 的子类需要这个。

-layout 一个非常好用的案例是在你需要精确一个子 node 的大小时。例如当你想要一个 collectionNode 占满全屏时。布局规格对这个案例支持不够好,通常最简单地是在这个方法里手动设置一行:

subnode.frame = self.bounds;

如果你期望在 ASViewController 里做出同样的效果,你可以在 -viewWillLayoutSubviews 里做同样的事。除非你的 node 是 initWithNode: 里的 node,在这种情况下,它将自动做这些。

ASViewController


ASViewController 是一个常规的 UIViewController 子类,兼具管理 node 的特殊功能。因为它是一个 UIViewController 子类,所有方法都在主线程上调用(并且你应该总是在主线程上创建 ASViewController)。

-init

在 ASViewController 生命周期的最初,这个方法被调用一次。如同初始化 UIViewController,最佳实践是在这个方法中永不访问 self.viewself.node.view,因为它会导致被迫提前创建视图。作为替代,在 -viewDidLoad 里进行任何视图访问。

ASViewController 的特定的初始化方法是 initWithNode:。一次典型的初始化看起来如以下代码。注意在调用 super 之前,ASViewController 的 node 是如何被创建的。ASViewController 管理 node 的方式类似于 UIViewController 管理 view,但是初始化略有不同。

SwiftObjective-C
- (instancetype)init
{
  _pagerNode = [[ASPagerNode alloc] init];
  self = [super initWithNode:_pagerNode];

  // setup any instance variables or properties here
  if (self) {
    _pagerNode.dataSource = self;
    _pagerNode.delegate = self;
  }

  return self;
}

-loadView

我们建议你不要使用这个方法,因为它相比 -viewDidLoad 没有什么特别的优点,还有一些缺点。但是只要你不将 self.view 属性设置为不同的值,就可以安全的使用它。调用 [super loadView] 会为你将它设置为 node.view

-viewDidLoad

这个方法在一个 ASViewController 的生命周期里只会被调用一次,紧随 -loadView 之后。这应该是你访问 node 的 view 最早的时候。这是一个很好的点去放置任何只应运行一次并请求访问 view/layer 的设置代码,例如添加手势识别。

样式代码绝对不应放置在这个方法里,因为几何变化时不会再次调用它。注意这同样适用于 UIViewController;即使你目前不期望几何变化,在这个方法里放置样式代码也是一个坏习惯。

-viewWillLayoutSubviews

这个方法的调用与 node 的 -layout 方法完全相同,并且在一个 ASViewController 的生命周期里可能会多次被调用;每当 ASViewController 的 node 改变边界时(包括旋转,分屏,键盘演示),以及改变结构时(子元素的添加,移除,或改变大小),它就被调用。

为了保持一致性,最佳实践是将全部样式代码放置在这个方法里。因为并不会经常调用它,即使并不直接取决于属于这里的大小的代码。

-viewWillAppear: / -viewDidDisappear:

这些方法只在 ASViewController 的 node 出现在屏幕之前(可被看见的最早时间)和从视图结构里移除之后(不再可见的最早时间)被调用。这些方法提供了很好的机会去开始或停止动画相关的展示或解除你(对 node)的控制权。这也是一个记录用户行为日志的好地方。

虽然这些方法可能会多次被调用并且几何信息也是可用的,但不是所有的几何变化都调用它们,也不应(在这些方法里)使用核心样式代码(超出特定动画所需的设置)。