作者:VincentMing,iOS开发,现在字节跳动剪映团队搬砖
Session:https://developer.apple.com/videos/play/wwdc2020/10167/
iOS14 给 UICollectionView 带来了全新的特性,可以大致分为三类:如何生成数据,如何定义布局以及如何显示内容。
本文会着重讲述视图配置,包括用于给 cell 配置内容和定义样式的新 API。
首先来看看 iOS13 时我们是如何配置 TableView 的 cell:
在 iOS14 中我们通过下面的方式进行 cell 配置:
两者看起来挺相似的,只多了两行代码,我们仔细对比下。
第一件事是获取 cell 的默认内容配置,它总返回一个全新的配置,没有任何自定义的内容,但它拥有基于 cell 和 TableView 的默认样式。关于样式我们后续会展开讲。当下,我们只把图片和文字传入内容配置中。这一段和 iOS13 的做法很相似。最后一步,我们把刚才的配置传给 cell 的 contentConfiguration 属性。只要完成这一步, cell 就会更新并显示我们配置的图片和文字。而在这之前,所有对图片和文字的改动只影响存储在 content 变量中的配置副本。因为我们使用内容配置,所以我们从未直接碰触 UI 组件,如 ImageView 或 Label。所有的属性是在配置上操作的。
使用内容配置来管理 cell 有什么好处呢?其一就是,我们配置 TableViewCell 的代码和配置 CollectionViewCell 的代码是一样的。实际上,同样的代码可以用于任何视图,哪怕不是 cell,如 TabelView 的 header 和 footer。这是如何做到的?因为配置是可组合的。不像 UITableviewCell 那样把全部功能固化在 cell 类中,这些标准 cell 布局和外观作为独立的部件提供,可以直接插入支持它们的任何 cell 或视图中。
下图是使用 iPad 新边栏的应用截图,边栏的 cell 使用了相同的内容配置,但处于不同的状态,如正常态、选中态、高亮态、禁用态等。可以看出,这些 cell 的默认样式在不同状态下各不相同。通过使用配置来设置我们的 cell,可以自动获得所有这些不同的外观。
我们稍后将详细讨论其工作原理。至此我们已经领略了这些新配置,但是它们到底是什么?
什么是配置(Configuration)?
配置描述了视图在某种特定状态下的外观,如内容,样式,指标和行为等。
配置只是一堆属性,只有将其应用于视图或cell并进行渲染,它们才会生效。
配置是可组合的,可用于任何支持它们的cell或视图。
目前有两种类型的配置:背景配置和内容配置。
背景配置有许多属性,能够快速指定常见的背景外观,如自定义背景填充色,设置模糊效果,设置描边和圆角。也可以通过提供自定义视图实现定制的效果。
列表内容配置不仅能为 cell、header、footer 提供标准布局(类似于 UITableViewcell 的样式),还支持图片、文字、可选的二级文字,以及许多属性可供自定义配置,还提供了更高级的特性,如灵活的布局以支持大量文字的显示,特殊的布局模式以支持辅助功能下的可变大小文字。
这两种配置类型具有一些共同的设计原则。
最重要的原则之一是配置的轻量化,创建成本很低。它们是值类型,这意味着这个配置只属于你,你对配置所做的更改不会影响其他任何事情,除非你把配置应用到cell。
因为其轻量化,你应当总是创建一个全新的配置,例如获取 cell 的默认内容配置,无论是第一次配置 cell 还是后续更新 cell。使用配置时,你无需考虑过去的状态,也不必先拿到已有的配置再去修改它。每次都从全新的配置开始,修改好后应用到 cell。
这个工作流和以往的 UIKit 有所不同,但它其实彻底解放了开发者,它会自动查明哪些视图改变了,并高效地去更新,而无需开发者的介入。
配置不仅提供了针对大量状态的默认外观,而且基于健壮的底层机制,让开发者可以对各种状态定制外观。配置还提供了很多高级行为,只需要几行代码即可实现。比如想对背景做动画,只需在动画中设置新的背景配置即可。
配置不仅易用,而且其设计方式杜绝了一整个类型的 bug,尤其是在处理复杂的状态和转换时。当前应用的配置就是事实,且当应用新配置时,新配置会全部同时生效。
最后,配置是为性能而生的,这对确保平滑滚动尤为重要。因为 UIKit 负责管理视图和渲染,所以我们能够实现许多内部性能优化。
配置状态
配置状态表示用于配置 cell 和视图的各种输入。
UITraitCollection 中的 Trait 是确定如何设置视图的最常见输入之一。如 layout direction、size class、user interface style 等。
在 Trait 之上,cell 和视图还拥有各种状态,如选中态,或是作为拖拽操作的目标,或是临时被禁用。在这些状态之上,你的应用程序还拥有自定义状态。如通信软件需要知道一个消息是已归档还是已标记,而支付软件可能会显示哪个交易正在进行中。如果使用viewModel管理单元格,也可以将该viewModel视为自定义状态。各种状态如下图所示:
所有这些构成配置状态:一个各种特征、状态和用户自定义状态的集合。
那么您在哪里找到配置状态?集合或表视图中的每个单元格,页眉和页脚都有其自己的配置状态。如下图所示,有三个 cell,每个 cell 可能有不同的配置状态。
那么配置状态是什么样的呢?这里可以看到有两种配置状态:view 配置状态和 cell 配置状态。可以看到,view 配置状态有 Trait 集合,高亮选中等状态,以及自定义状态。
cell 配置状态除了包括上述状态,还有直属于 cell 的状态,如编辑、滑动、展开和拖拽。
更新配置
使用配置状态的重要操作之一是更新配置。
你可以根据不同的配置状态获得新的背景配置或内容配置,它是一个属性已经更新到最新状态的配置副本。当您请求更新的配置时,原始配置不会更改。并且,如果您在原始配置上自定义了任何属性,则这些属性在更新后的配置上将保持不变。属性设置好了后,就像被锁定了一样。
回顾之前的演示,可以看到内容配置会根据不同状态更新外观,这是因为有自动配置更新。默认情况下,当你对 cell 设置了背景或内容配置时,只要其配置状态发生变化,就会自动更新该配置,并将新配置应用于 cell。下图中的这些属性可以控制自动更新行为。
如果你希望获得各种状态的默认样式,那么使用自动更新即可。如果要自定义不同状态的外观,则可以禁用自动更新并手动更新配置。但是更新配置的代码应该写在哪里呢?我们在 CollectionView 和 TableView 的 cell 上新增 updateConfiguration(using state: UICellConfigurationState) 方法。你需要在 cell 的子类中重写该方法,根据传入的配置状态来更新 cell 的外观。该方法总是在 cell 被首次显示之前调用,且只要配置状态更改,就会再次调用此方法,使得 cell 一直处于最新状态。
当使用配置时,无需担心旧状态。每次只需获取新的配置,设置其属性,然后将其应用于 cell 即可。当代码中只有一处可以更新配置并将其应用到 cell 时,配置才能发挥最佳作用。如果你使用的是自定义 cell 子类,则 updateConfiguration(using state: ) 方法是执行上述操作的最佳选择,同时也最适合集中处理任何其他设置或更新。
例如,如果你使用的是最新的 CollectionViewListCell,则可以通过此方法为不同状态更新 cell 附件的 tint 颜色。如果需要重新配置单元,只需调用 setNeedsUpdateConfiguration() 方法。
让我们看一个示例,如何使用该方法设置 cell 并手动更新不同状态的配置。首先通过 cell 的默认内容配置获得全新的配置,然后将其更新到新的状态,这将为我们提供新状态的系统默认样式配置。然后我们根据 cell 将要显示的内容设置配置的图像和文本。接下来准备应用自定义属性。检查当前状态是突出显示还是选中,如果是,则将图像和文本颜色设置为白色。对于其他任何状态,我们没有设置颜色,即使用了配置的默认颜色。最后,我们把新配置应用到 cell。每当状态变更,都会再次调用此方法,并为新状态应用新配置。这就是自定义不同状态下外观的内容配置的方法。
我们同样可以在此处自定义背景配置。在此示例中,我们为某些状态下的图像和文本设置了不同的颜色。但还有另一种方法,可以使用一种称为“颜色转换器”的新类型来更改不同状态下的颜色外观。彩色转换器接受一种颜色,再以某种方式修改颜色并返回。
例如某个颜色转换器可以返回任意颜色的灰度。因此,颜色转换器只是简单的函数映射,但它却非常强大,因为你可以使用不同的颜色转换器把同一个颜色转换成不同的颜色。这就是通过颜色转换器为不同状态创建不同外观颜色的方法。某些样式和状态的某些默认配置将具有预设的颜色转换器,以便为该状态生成特定的外观。变色器与输入颜色结合使用,可以产生你需要的颜色。
需要注意的细节
知道了如何通过配置为不同状态生成新的外观之后,我们来了解下使用背景和内容配置时需要注意的一些细节。CollectionView 的 ListCell,TableView 的 cell、header 和 footer 都能根据包含的 List 或 TableView 的样式自动设置其默认的背景配置。因此通常情况下,你无需做任何事就能获得想要的背景样式。
内容配置则与此有所不同。cell 不会自动应用一个内容配置,而你可以对 cell 使用 defaultContentConfiguration 方法来获得基于 cell 当前样式的全新配置,就像前面例子那样。对于背景配置或内容配置,你可以通过 UIBackgroundConfiguration 和 UIListContentConfiguration 轻松获得任意样式下的默认配置。
再来看个侧边栏列表的例子。如图所示,所有的 cell 都具有与侧边栏样式相匹配的默认背景和内容配置。当选中某一个 cell 时,背景样式和内容样式会随之改变。
同样的列表,可以使用不同的样式,如分组外观、内嵌分组外观、普通外观等。这些外观和 UITableView 中的很相似。collectionView list 支持新的外观,如两种侧边栏样式。
上述的所有不同列表外观来自默认的背景和内容配置。
默认配置还支持动态大小,如下图所示。
当使用 accessibility 最大字号时,特殊的 cell 布局使得文字会围绕图片显示,来最大化利用空间。内容配置从底层向上,支持这样的动态布局。
我们来快速了解一下其底层实现机制。内容配置适用于自调节大小的 cell,其高度可以根据具体配置和环境灵活调整。这有助于你的应用在不同尺寸的设备、不同的动态大小下展示不同数量和类型的内容时都能好看。
通过内容配置,可以控制下图中蓝色的 margin 和橙色的 padding 属性。这些属性和实际展示内容一起决定了固有高度,影响 cell 如何自调整大小。因此,你不该使用固定高度,而应该通过使用这些属性让 cell 自动调节大小。
您还应该了解另一种布局概念。
如上图所示,当更换了不同宽度的图片时,原本对齐的列表变得不对齐了,因为图片是左对齐的,且每行文字和图片之间有同样的间距。
解决的方法是设定每个图片的保留大小(红线之间),再让图片在保留区域水平居中。如果图片比保留大小更大,则允许其从保留区域伸展出来。文字也对齐了,因为它们与图片的保留区域末端保持相同的距离。
如果你使用 Symbol 图片,UIKit 已经帮你应用了标准保留大小;对于其他图片,你可以自行设置其保留大小。
其他值得注意的点
配置和一些已有属性是互斥的。如应用背景配置总会重置背景颜色,且置空背景视图属性,反之亦然。所以你不能把背景配置和其他设置背景属性的代码混用。特别是使用 UITableView。
内容配置则取代了 cell、header 和 footer 的内置子视图,如图片、文本标签、详细文本标签等。这些老的内容属性会在未来的版本被弃用。我们鼓励你使用内容配置以获得强大的特性和可定制性。
除了列表内容配置,你还可以使用 ListContentView,用配置创建或更新该视图,再把它和你的自定义视图组织到一起。
ListContentView 只是普通的 UIView,你可以在任意地方使用它,即使不在 CollectionView 或 TableView之中,如在 UIStackView 中使用。
即使你在 cell 中打造一个完全自定义的视图结构,系统配置仍然能帮到你。因为配置非常轻量化,你可以把它用作字体、颜色、间距的默认值拷贝到自定义视图,哪怕你从不直接应用该配置。
对于更高阶的使用场景,你可以创建一个完全自定义的内容配置类型,以及渲染它的内容视图类,然后就像使用列表内容配置一样去使用你的自定义配置。使用自定义配置,可以充分利用前面提到的所有特性,包括允许自定义配置自动更新到新的状态。不管你的 cell 多么复杂,你都能从强大的配置中受益。
以上就是现代 cell 配置的全部基础内容了。
总结
总结一下,背景配置和内容配置非常易用,你可以用同样的代码去配置 CollectionView cell 或 TableView cell。
配置让你专注于你想展示什么,而不必操心于如何更新所有的视图。
每次都从全新的配置开始,设置成想要的样子,应用到 cell,让 UIKit 去代劳剩下的一切。
你已经知道如何使用配置状态去更新配置,从而生成一系列不同的样式。
你可以利用配置状态去设置 cell。
今天讲到的一切从设计层面具有可组合性和可扩展性,便于满足你的需求。去下载样例工程尝试新的 API 吧。
推荐阅读
如何使用原生 Diff 能力优化 UICollectionView 性能
关注我们
我们是「老司机技术周报」,每周会发布一份关于 iOS 的周报,也会定期分享一些和 iOS 相关的技术。欢迎关注。
关注有礼,关注【老司机技术周报】,回复「2019」,领取学习大礼包。
支持作者
这篇文章的内容来自于 《WWDC20 内参》。在这里给大家推荐一下这个专栏,专栏目前已经创作了 105 篇文章,只需要 29.9 元。点击【阅读原文】,就可以购买继续阅读 ~
WWDC 内参 系列是由老司机周报、知识小集合以及 SwiftGG 几个技术组织发起的。已经做了几年了,口碑一直不错。 主要是针对每年的 WWDC 的内容,做一次精选,并号召一群一线互联网的 iOS 开发者,结合自己的实际开发经验、苹果文档和视频内容做二次创作。