关于自定义瀑布流的布局(Swift)

瀑布流 UICollectionView Swift


最近整理以前iOS开发中用到的功能代码,发觉对瀑布流的布局有点新的认识,现记述如下,也方便对此不熟悉的小伙伴们作以参考。
关于瀑布流布局,我有两种思路,一种是使用UIScrollView,一种是使用UICollectionView,但UIScrollView的话,要考虑每个cell的循环复用问题,带来的开发问题比较多。如果使用UICollectionView,就会很方便地实现瀑布流布局

思路

  • 通过UICollectionView的特性,我们可以容易实现cell的布局,也不需要考虑循环复用的问题,但每个cell的布局都是一样的,frame是固定死的。不过深入研究后,决定UICollectionView布局的是UICollectionViewLayout这个类。
  • 那么我们是否可以重写UICollectionViewLayout的子类UICollectionViewFlowLayout来自定义布局呢
  • 答案显然是可以的,我们可以仿照UICollectionViewFlowLayout重新定义一个瀑布流布局。如果这样,显然要耗费不少时间,而且也没有必要,这里,我重新定义一个布局,继承自UICollectionViewFlowLayout,重写一些函数和属性,来实现瀑布流布局

重要代码

UICollectionView布局的时候,总是会调用prepare()函数,那么我们重写这个函数:

override func prepare() {
        super.prepare()
        
        guard let collectionView = collectionView else {
            return
        }
        let count = collectionView.numberOfItems(inSection: 0)
        
        let itemW = (collectionView.bounds.width - sectionInset.left - sectionInset.right - (CGFloat(cols) - 1) * minimumInteritemSpacing) / CGFloat(cols)
        var itemH: CGFloat = 0
        var itemX: CGFloat = 0
        var itemY: CGFloat = 0
        
        for i in cellAttributes.count..

cell中布局的实质是UICollectionViewLayoutAttributes决定着每个cell的frame,那么就需要重写layoutAttributesForElements()这个函数,用来返回我们自定义的布局:

 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return cellAttributes
 }

collectionView 之所以能滚动是因为有滚动范围,那么我们依然重写collectionViewContentSize这个计算型属性:

override var collectionViewContentSize: CGSize {
        return CGSize(width: 0, height: colHeight.max()! + sectionInset.bottom - minimumLineSpacing)
}

除此之外,还需要定义一些私有和公开属性:

    /// 数据源
    weak var dataSource: TCWaterfallLayoutDataSource?
    /// 列数
    fileprivate lazy var cols: Int = self.dataSource?.waterfallLayoutWithCols?(self) ?? 2
    /// cell属性数组
    fileprivate lazy var cellAttributes: [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
    /// 缓存高度
    fileprivate lazy var colHeight: [CGFloat] = Array(repeatElement(self.sectionInset.top, count: self.cols))

既然有数据源属性,那么就得有数据源协议,这里定义了2个函数,一个返回cell高度,一个返回的是UICollectionView显示的列数:

@objc protocol TCWaterfallLayoutDataSource: class {
    func waterfallLayoutForHeight(_ layout: TCWaterfallLayout, indexPath: IndexPath) -> CGFloat
    @objc optional func waterfallLayoutForCols(_ layout: TCWaterfallLayout) -> Int
}

如此,创建UICollectionView 的时候,就可以把布局换成自定义的布局了,实现数据源,就会有以下效果:

关于自定义瀑布流的布局(Swift)_第1张图片
效果图.png

如有错误,还望指正!

你可能感兴趣的:(关于自定义瀑布流的布局(Swift))