PluginLayout
Plugin布局是一个设计用于为每个UICollectionView
的section提供特定布局配置的UICollectionViewLayout
。
对于每个section,布局会请其delegate
提供一个合适的Plugin
,该Plugin
将创建合适的UICollectionViewLayoutAttributes
并协助确定最终内容的尺寸。
TLDR
PluginLayout
提供了将不同的方式组合起来,为每个collection view的section进行布局的可能性。
它还提供了一些真正出色的UICollectionViewLayout
实现,开箱即用,完全兼容UICollectionViewFlowLayout
和UICollectionViewDelegateFlowLayout
。
这意味着您可以替换当前的flow布局与提供的其中一个,如果需要,实现一些额外的delegate方法,然后就可以发布了。
关键概念
如同正常的UICollectionViewLayout
,PluginLayout
遵循了通常的3个阶段来计算每个最终的UICollectionViewLayoutAttributes
,这些UICollectionViewLayoutAttributes
将被用到来放置(和重用)每个cell到可用的内容空间内。
在布局的prepare()
阶段,主要的布局请求每个可用的插件创建所有属性,并给每个属性提供适当的frame
。
每个Plugin
必须实现
func layoutAttributes(in section: Int, offset: inout CGPoint, layout: PluginLayout) -> [PluginLayoutAttributes]
其中,offset
属性(声明为 inout
)存储当前布局的全局内容大小。如果当前插件需要使用一个超出当前偏移位置的框架放置新的属性,则需要更新 offset
位置,以便宿主 PluginLayout
能够正确地设置最终内容的大小。
collectionViewContentSize()
阶段简单地将缓存的 offset
值用于设置内容大小。该值将代表所有插件生成的属性相交的底部最右端的 CGPoint
。
注意:为了性能原因,默认属性在内部缓存,直到 UICollectionView
改变其外部边框(旋转)或直到下一次 reloadData()
。
插件
Plugin
是一个能够生成和操作特定 UICollectionViewLayoutAttributes
元素的章节内的对象。
插件负责这些元素在其章节内的放置方式以及用于生成它们的类(只要它是 PluginLayoutAttributes
的子类)。这通常通过在每个属性上设置一个 frame
属性来完成。
属性可以代表普通单元格、辅助视图或装饰视图。
当 UICollectionView
滚动时,如果在 Effect
对象内部不能表达这种类型的排列,则插件也负责对每个属性自身的任意排列。
效果
Effect
是一个对象,能够在集合视图滚动期间独立于任何最初生成它的插件来处理已生成的 UICollectionViewLayoutAttributes
。
我们可以将集合视图和表视图的通常“粘性头部/头部固定”视为一个完全独立于 如何 和 在哪里 插件最初放置该属性的效果:具有粘性效果的头(或尾)部将始终固定在集合视图的顶部/底部部分,而不管它们的起始位置如何。
结合 Plugin
和 Effect
对象可以带来完全新的(并且令人惊叹的)布局,这些布局的特性是完全独立于对方的,更重要的是,可以在不同的环境中重复使用。
在将多个效果应用于属性时,应用顺序很重要。
包含布局
每个布局都必须视为一个单一的插件
,它可以与其他插件一起使用,以及一个完全配置好的PluginLayout
子类,以便在没有额外配置的情况下,适用于应用程序的每个部分都应该有相同布局行为的所有情况。
每个布局都有一个通用的委托
属性,它必须实现UICollectionViewDelegateFlowLayout
。
FlowLayout
FlowLayout
是原始的UICollectionViewFlowLayout
的完整替代品。它模仿原始的苹果行为,为每个部分创建与原始行为相同的标题和页脚。在以下情况下,需要使用FlowLayoutPlugin
:一些部分的集合视图中应保留原始流布局行为,而其他部分则分配不同的插件。
完整的FlowLayout
类可能看似没有意义,因为最终它是对经过战斗检验的UICollectionViewFlowLayout
的逆向工程复制;然而,它与Effect
完全兼容,需要实现与滚动相关的布局效果时应该使用它。
此插件的委托
属性是标准的UICollectionViewDelegateFlowLayout
。
GridLayout
GridLayoutPlugin
是FlowLayoutPlugin
的子类,其中元素按照网格排列。每个元素可以接受可用宽度的分数(用于垂直滚动布局)或可用高度的分数(用于水平滚动布局),而其他维度根据由委托提供的特定aspectRatio
进行计算。
此插件的委托
属性必须实现两个额外的方法
func collectionView(_ collectionView: UICollectionView, layout: PluginLayout, lineFractionAt indexPath: IndexPath) -> Int
给定布局中的单个行,考虑到其项之间的间距和部分内边距,此值用于将可用的总行空间(垂直布局中的宽度,水平布局中的高度)划分,以便将该值应用于单个项的维度。
示例:在垂直滚动布局中,将每个项的值返回为2
将结果为两列布局。一个1
的值将结果为UITableView
样式布局。
func collectionView(_ collectionView: UICollectionView, layout: PluginLayout, aspectRatioAt indexPath: IndexPath) -> CGFloat
每个项期望的宽高比。在垂直布局中,此值用于计算每个项的高度
(width / ratio
)。在水平布局中,此值用于计算每个项的宽度
(height * ratio
)
StaggeredLayout
交错布局通过预定义数量的行(垂直滚动布局中的列)来构建,这些行由具有特定尺寸的项目独立填充。行/列通过遍历项目总数并将每个项目放置在下一个可用的列中来填充;由于项目尺寸之间可能会有很大差异,因此在结束时没有保证的对齐,但每行/列的项目总数将相同(如果总数是列数的倍数,否则第一列和最后几列之间的元素差为1)。
交错布局的一个典型例子是Pinterest应用,它被设计为拥有无限数量的项目。在这种情况下,底部部分的不对齐不是一个大问题,因为很少会滚动到底部。
马赛克布局
马赛克布局适用于那些具有期望大小且不需要精确大小的项目。我们的算法灵感主要来自Lightbox算法和这篇优秀的文章。
布局通过精确的行/列数来构建;每行/列通过等间距的列/行来分割,创建一个不可见的“网格”。为了说明这一点,让我们假设一个垂直滚动布局,由列组成。
每个项目都提供期望的宽高比,通过分配当前列宽将其放置在适当的列中,然后将高度四舍五入到下一行,使宽高比尽可能得到尊重。
如果前一列在相同的行结束且是连续的,下个项目的宽度将设置以覆盖所有连续的列,而其高度将根据宽高比缩放并四舍五入。
下一个项目始终放置在顶部可用的列中,这样整体底部行可以最均匀地增长。