objc系列译文(5.2):UICollectionView 和 UIKit Dynamics

1576 查看

UIKit Dynamics 是 iOS 7 中基于物理动画引擎的一个新功能–它被特别设计使其能很好地与 collection views 配合工作,而后者是在 iOS 6 中才被引入的新特性。接下来,我们要好好看看如何将这两个特性结合在一起。

这篇文章将讨论两个结合使用 UIkit Dynamics 和 collection view 的例子。第一个例子展示了如何去实现像 iOS 7 里信息 app 中的消息泡泡的弹簧动效,然后再进一步结合平铺机制来实现布局的可伸缩性。第二个例子展现了如何用 UIKit Dynamics 来模拟牛顿摆,这个例子中物体可以一个个地加入到 collection view 中,并和其他物体发生相互作用。

在我们开始之前,我假定你们对 UICollectionView 是如何工作是有基本的了解——查看这篇 objc.io 文章会有你想要的所有细节。我也假定你已经理解了 UIKit Dynamics 的工作原理–阅读这篇博客,可以了解更多 UIKit Dynamics 的知识。

文章中的两个例子项目都已经在 GitHub 中:

关于 UIDynamicAnimator

支持 UICollectionView 实现 UIKit Dynamics 的最关键部分就是 UIDynamicAnimator。要实现这样的 UIKit Dynamics 的效果,我们需要自己自定义一个继承于 UICollectionViewFlowLayout 的子类,并且在这个子类对象里面持有一个 UIDynamicAnimator 的对象。

当我们创建自定义的 dynamic animator 时,我们不会使用常用的初始化方法 -initWithReferenceView: ,因为我们不需要把这个 dynamic animator 关联一个 view ,而是给它关联一个 collection view layout。所以我们使用 -initWithCollectionViewLayout: 这个初始化方法,并把 collection view layout 作为参数传入。这很关键,当的 animator 的 behavior item 的属性应该被更新的时候,它必须能够确保 collection view 的 layout 失效。换句话说,dynamic animator 将会经常使旧的 layout 失效。

我们很快就能看到这些事情是怎么连接起来的,但是在概念上理解 collection view 如何与 dynamic animator 相互作用是很重要的。

Collection view layout 将会为 collection view 中的每个 UICollectionViewLayoutAttributes 添加 behavior(稍后我们会讨论平铺它们)。在将这些 behaviors 添加到 dynamic animator 之后,UIKit 将会向 collection view layout 询问 atrribute 的状态。我们此时可以直接将由 dynamic animator 所提供的 items 返回,而不需要自己做任何计算。Animator 将在模拟时禁用 layout。这会导致 UIKit 再次查询 layout,这个过程会一直持续到模拟满足设定条件而结束。

所以重申一下,layout 创建了 dynamic animator,并且为其中每个 item 的 layout attribute 添加对应的 behaviors。当 collection view 需要 layout 信息时,由 dynamic animator 来提供需要的信息。

继承 UICollectionViewFlowLayout

我们将要创建一个简单的例子来展示如何使用一个带 UIkit Dynamic 的 collection view layout。当然,我们需要做的第一件事就是,创建一个数据源去驱动我们的 collection view。我知道以你的能力完全可以独立实现一个数据源,但是为了完整性,我还是提供了一个给你:

我们注意到当 view 第一次出现的时候,这个 layout 是被无效的。这是因为没有用 Storyboard 的结果(使用或不使用 Storyboard,调用 prepareLayout 方法的时机是不同的,苹果在 WWDC 的视频中并没有告诉我们这一点)。所以,当这些视图一出现我们就需要手动使这个 collection view layout 无效。当我们用平铺(后面会详细介绍)的时候,就不需要这样。

现在来创建自定义的 collection view layout 吧,我们需要强引用一个 dynamic animator,并且使用它来驱动我们的 collcetion view layout 的 attribute。我们在实现文件里定义了一个私有属性:

我们将在 layout 的初始化方法中初始化我们的 dynamic animator。还要设置一些属于父类 UICollectionViewFlowLayout 中的属性:

我们将实现的下一个方法是 prepareLayout。我们首先需要调用父类的方法。因为我们是继承 UICollectionViewFlowLayout 类,所以在调用父类的 prepareLayout 方法时,可以使 collection view layout 的各个 attribute 都放置在合适的位置。我们可以依靠父类的这个方法来提供一个默认的排布,并且能够使用 [super layoutAttributesForElementsInRect:visibleRect]; 方法得到指定 rect 内的所有 item 的 layout attributes。