目录
项目下载地址: CollectionView-Note
UICollectionView 01 - 基础布局篇
UICollectionView 02 - 布局和代理篇
UICollectionView 03 - 自定义布局原理篇
UICollectionView 04 - 卡片布局
UICollectionView 05 - 可伸缩Header
UICollectionView 06 - 瀑布流布局
UICollectionView 07 - 标签布局
我们在使用系统的 UICollectionViewFlowLayout
时,只需要告诉它元素大小、间距、滚动方向之类的就能看到一个流式布局,那是因为它根据我们提供的信息,帮我们计算了每个元素的布局信息。这个布局信息存放在 UICollectionViewLayoutAttributes
。 所以如果我们自定义布局的时候这些就需要我们自己计算,然后计算完最好缓存起来避免重复计算,然后在界面需要展示的时候把这个信息传递过去。 当然我们的自定义布局可以基于 UICollectionViewFlowLayout
做一些调整,也可以直接继承自 UICollectionViewLayout
完全自定义布局。
自定义布局有三个必须实现的方法 :
class CustomLayout: UICollectionViewLayout {
override func prepare() {
// 准备布局
}
override var collectionViewContentSize: CGSize {
return CGSize(width: width, height: contentHeight)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return someAttributes
}
}
-
prepare
: 预备阶段,在这里进行一些初始化或者布局信息的计算和缓存操作 -
collectionViewContentSize
:collectionView
必须知道自己的内容大小来决定滚动区域 -
layoutAttributesForElements(in:)
: 可见区域,当快要滚动到这个区域或者说这个区域准备渲染的时候,collectionView就会通过这个方法来获取cell
或者Supplementary View
的布局信息。
UICollectionViewLayoutAttributes
中包含非常多的布局属性。
open var frame: CGRect
open var center: CGPoint
open var size: CGSize
open var transform3D: CATransform3D
@available(iOS 7.0, *)
open var bounds: CGRect
@available(iOS 7.0, *)
open var transform: CGAffineTransform
open var alpha: CGFloat
open var zIndex: Int // default is 0
open var isHidden: Bool
open var indexPath: IndexPath
如果你觉得这个属性不够用,你还可以继承他 实现自己的 Attributes 。
下面看下 UICollectionViewLayout
除了那三个方法,还为我们提供了哪些常用的属性和方法(一些不常用的暂且忽略,因为我也没用过...)。
类属性 layoutAttributesClass
, 重写这个返回我们自定义的 Attributes 就可以实现我上面说的自定义 Attributes了。
layoutAttributesForItem(at: ) -> UICollectionViewLayoutAttributes?
可以重写这个方法,返回每个indexPath对应的attribute
layoutAttributesForSupplementaryView(ofKind: at: ) -> UICollectionViewLayoutAttributes?
重写此方法返回indexPath对应的SupplementaryView , 后面写粘性sectionHeader ,和整个 UICollectionView
的Header 会用到此方法。
其实还有一个DecorationView
,对section的背景之类的进行装饰。 比如对每个section设置不同的背景色什么的,这时你需要用到如下方法
layoutAttributesForDecorationView(ifKind: at:) -> UICollectionViewLayoutAttributes?
shouldInvalidateLayout(forBoundsChange: )-> Bool
此方法传入新的bounds,可以根据次bounds决定是否需要重新计算布局。 返回true代表重新计算,比如 IndexPath(item: 0,section: 0)
我们已经计算过了,它被滑动出了屏幕外,等再次滑动进来的时候是否还需要重新计算,如果计入视线的时候我们是根据位置实时变幻的 那就需要每次都计算,这个根据实际情况而定。
下面几个方法是在元素被添加、删除等的时候做一些动画的
// 此方法用来表示 执行的Action和操作的indexPath
open func prepare(forCollectionViewUpdates updateItems: [UICollectionViewUpdateItem])
// 一般用来做insetItem的动画的
open func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?
// 一般用来做deleteItem的动画的
override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes?
以上这些理论看着着实有些枯燥,后面几篇开始实战。上面的大部分方法后面都会用到。