UICollectionViewLayout
UICollectionViewLayout向UICollectionView提供布局信息,不仅包括cell的布局信息,也包括追加视图和装饰视图的布局信息。
实现一个自定义layout的常规做法是继承UICollectionViewLayout类。下面简单介绍与cell相关的三个重要方法(还有其他的)。
func prepare()
布局调用的第一个方法,如果collectionView重新加载时,也会再次调用。
必须实现父类的该方法。
func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
返回用于补充或装饰视图的布局属性,或以屏幕方式按需进行布局。
该方法调用比较多
func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
根据需要返回特定索引路径的布局属性实例。
官方文档说,该方法should implement。但是个人测试后觉得一般最好不实现的,并且该方法在系统内部不会被调用,只用于辅助自己实现的例如func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
内部可能调用该方法。
实现瀑布流
这里代码,并且带有代码非常简短,所以大概讲一下逻辑,直接贴出代码算了。
- 确定上下左右的内缩距离,UICollectionViewFlowLayout自身提供了sectionInset
- 确定列数与layoutAttributes的个数
- 计算各个layoutAttributes的frame
- 细节上:记住每一列的最高高度,下一个cell将添加在高度最小的那一列。考虑上拉刷新、下拉加载等情况,cell的数量会变
protocol LWWaterfallsFlowLayoutDataSource: class {
func numberOfCols(in layout: LWWaterfallsFlowLayout) -> Int
func waterfallsFlowLayout(_ layout: LWWaterfallsFlowLayout, heightAt item: Int) -> CGFloat
}
class LWWaterfallsFlowLayout: UICollectionViewFlowLayout {
weak var dataSource: LWWaterfallsFlowLayoutDataSource?
fileprivate lazy var attrArr = [UICollectionViewLayoutAttributes]()
fileprivate lazy var cols : Int = {
return self.dataSource?.numberOfCols(in: self) ?? 3
}()
fileprivate lazy var totalHeights : [CGFloat] = Array(repeating: self.sectionInset.top, count: self.cols)
override func prepare() {
super.prepare()
print("prepare")
let itemCount = collectionView!.numberOfItems(inSection: 0)
let attrW : CGFloat = (collectionView!.bounds.width - sectionInset.left - sectionInset.right - CGFloat(cols - 1) * minimumInteritemSpacing) / CGFloat(cols)
for item in attrArr.count..<itemCount {
let attr = UICollectionViewLayoutAttributes(forCellWith: IndexPath(item: item, section: 0))
guard let attrH : CGFloat = dataSource?.waterfallsFlowLayout(self, heightAt: item) else {
fatalError("请实现对应的数据源方法,并且返回Cell高度")
}
let minH = totalHeights.min()!
let minIndex = totalHeights.index(of: minH)!
let attrX : CGFloat = sectionInset.left + (minimumInteritemSpacing + attrW) * CGFloat(minIndex)
let attrY : CGFloat = minH
attr.frame = CGRect(x: attrX, y: attrY, width: attrW, height: attrH)
attrArr.append(attr)
totalHeights[minIndex] = minH + minimumLineSpacing + attrH
}
}
// 在这里该方法不会被调用
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
print("layoutAttributesForItem")
return attrArr[indexPath.item]
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
print("layoutAttributesForElements")
return attrArr
}
override var collectionViewContentSize: CGSize {
return CGSize(width: 0, height: totalHeights.max()! + sectionInset.bottom - minimumLineSpacing)
}
}
网友评论