UICollectionViewLayoutInvalidationContext性能优化 详细流程图 + 范例
起步基础
UICollectionViewLayout 基本使用
UICollectionViewLayoutAttributes
Attributes赋值
这里泛指了以下两个主函数,就不在赘述两个功能,以及 UICollectionViewLayoutAttributes 需处理的变量。
1 | class AutoSizingLayout: UICollectionViewLayout { |
Without Invalidating
先来看看在 没有需要重新改变Attributes 下的流程(以下简称 配置流程):
data reload时,prepare计算一次,layoutAttributesForElements
调用多次直到,系统已经有所有IndexPath的atrribute,就不会在调用这些functiom,直到collectionView reload。
With Invalidating
现在我们把失效的概念加进来
强制失效 UICollectionViewLayout.invalidateLayout()
invalidateLayout()可随时呼叫,他会将所有系统已取得的 Attribute 全部标记为 invalid 并舍弃。
准确的update时机并不是调用后,而是在下一次 layout 的 update Cycle里后,重新调用prepare. 堆栈如图:
https://developer.apple.com/documentation/uikit/uicollectionviewlayout/1617728-invalidatelayout
若有overrider此方法,必须call super.invalidateLayout()
条件失效 UICollectionViewLayout.shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool
默认回传 false。
overrider后,可借由传入的 newBounds 判断是否需要 invalidLayout,若回传 true 则跟 InvalidateLayout()之后的流程(堆栈)相同。
例如内容下半部,需要不断更新Attribute
1 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { |
newBounds: CGRect
此bounds的触发时机,为collectionView可视范围改动时(contentOffset Change)
为了简化流程图,我们将固定一起出现的这几个步骤,划成一个:
UICollectionViewLayoutInvalidationContext
(以下简称 InvalidationContext)
https://developer.apple.com/documentation/uikit/uicollectionviewlayoutinvalidationcontext这的context跟出现在其他地方的Context上下文概念差不多,先借由一个function的参数,对此上下文进行设置,回传后再下一个function对设置的内容进行处理。
基于系统『原生』的 InvalidationContext 失效layout
这里再多覆写了两个函数
invalidationContext(forBoundsChange:)
可以借由参数 bounds 对context 进行部分逻辑处理,也可在这做 『失效标记』
1 | override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext { |
invalidateLayout(with:)
根据上一部处理好的逻辑或 『失效标记』 做属性处理,必调用
super.invalidateLayout(with: invalidationContext)
1
2
3override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) {
//以context的资讯,做些update
}
系统提供可用的失效标记,无任何标记将会重新进入『配置流程』
这些标记的功用,是在第一个function根据bounds作上标记,在第二个funciton中可以根据以下对应的变数取得当初的标记,做对应的『局部属性预处理』。
有标记可做 局部属性预处理 ,并会被重新询问 Attribute
重新询问 Attribute
例如:若滑超过 1/2 Y,使 row 17 失效。
1 |
|
覆写的此function就会被调用并询问 row17 的attribute
1 | override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { |
局部属性预处理
局部属性预处理 其实就是为了上一步 『重新询问 Attribute』这块做预先计算,
例如:若滑超过 1/2 Y,使 某Decoration失效。
1 | override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext { |
并在 invalidateLayout(with:) 从context里查询是否对应的Decoration包含在失效名单内,并提前计算好心的Attribute存在持有变量
1 | override func invalidateLayout(with context: UICollectionViewLayoutInvalidationContext) { |
在询问的时候,将预先计算好的Attribute 回传
1 |
|
特殊标记 (get-only)
这两个特殊标记是会在触发 collectionView.reloadData()时会被系统自动启用,不能自己设置,并且仍会重新进入『配置流程』。
基于系统『自定义』的 InvalidationContext 失效layout
第一步当然是写一个继承UICollectionViewLayoutInvalidationContext的类,并且在UICollectionViewLayout类里覆写以下
1 | override class var invalidationContextClass: AnyClass { |
自定义 InvalidationContext 的好处不外乎就是能自己增加字段,能更清晰也更有逻辑的衔接前后两个函数
例如:沿用前面的例子,但InvalidationContext为自定义,增加一个Bool,判断哪部分需要失效或是需要被标记
1 | class LayoutInvalidationContext: UICollectionViewLayoutInvalidationContext { |