效果图如下:
1、自定义瀑布流布局
要想实现collectionView的任意格式的瀑布流布局,需要自定义布局
下面的方法是在自定义LGWaterfullLayout中实现的
// 通过数据源协议,获取外部传过来的高度
protocol LGWaterfullLayoutDataSource: class {
func waterfullLayout(_ layout: UICollectionViewFlowLayout, itemIndex: Int) -> CGFloat
}
class LGWaterfullLayout: UICollectionViewFlowLayout {
// 暴露接口给外面,列数,默认为3
var cols = 3
// 数据源协议,用于接收外面传过来的cell高度
weak var dataSource : LGWaterfullLayoutDataSource?
// 布局最重要的部分,attributes保存所有cell的frame
fileprivate lazy var attributes : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
// 设置collectionView默认的ContentSize高度
fileprivate lazy var maxHeight : CGFloat = self.sectionInset.top + self.sectionInset.bottom
// 保存collectionView每一列的总高度,因为下一个cell总是放在当前高度最低的地方
fileprivate lazy var heights : [CGFloat] = Array(repeating: self.sectionInset.top, count: self.cols)
}
2.准备cell的布局
override func prepare() {
// 1.校验collectionView有没有值
guard let collectionView = collectionView else {
return
}
// 2.获取cell的个数,此处只有一个section
let count = collectionView.numberOfItems(inSection: 0)
// 3.遍历所有的cell,给每一个cell准备一个UICollectionViewLayoutAttributes
// 获取cell的宽度,此宽度为固定的
let itemW = (collectionView.bounds.width - sectionInset.left - sectionInset.right - (CGFloat(cols) - 1) * minimumInteritemSpacing) / CGFloat(cols)
// 遍历所有cell,每次都从新加载的开始布局,之前布局过的跳过,此处主要是为了上拉加载更多的需要
for i in attributes.count..<count {
// 3.1 创建UICollectionViewLayoutAttributes,并且传入indexpath
let indexPath = IndexPath(item: i, section: 0)
let attribute = UICollectionViewLayoutAttributes(forCellWith: indexPath)
// 3.2 给attribute设置frame
// 校验高度是否存在,不存在默认设为100
let itemH = dataSource?.waterfullLayout(self, itemIndex: i) ?? 100
// 获取heights数组中的最小值
let minH = heights.min()!
// 找到最小值对应的索引
let minIndex = heights.index(of: minH)!
// 根据索引设置cell的X坐标
let itemX = sectionInset.left + (minimumInteritemSpacing + itemW) * CGFloat(minIndex)
// 设置cell的Y坐标
let itemY = minH
// 设置attribute的frame,决定cell的位置
attribute.frame = CGRect(x: itemX, y: itemY, width: itemW, height: itemH)
// 3.3 将attribute添加到数组中
attributes.append(attribute)
// 3.4 改变minIndex位置的高度
heights[minIndex] = attribute.frame.maxY + minimumLineSpacing
}
// 4.获取最大高度,用于设置ContentSize的滚动高度
maxHeight = heights.max()!
}
3.获取准备好的布局
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attributes
}
4.获取滚动的范围
override var collectionViewContentSize: CGSize {
return CGSize(width: 0, height: maxHeight)
}
5、在ViewController里创建collectionView瀑布流
下面内容在ViewController里实现
1、创建collectionView,并设置参数、代理和数据源
fileprivate lazy var collectionView : UICollectionView = {
let layout : LGWaterfullLayout = LGWaterfullLayout()
// 设置行间距
layout.minimumLineSpacing = 10
// 设置列间距
layout.minimumInteritemSpacing = 10
// 设置collectionView距离上下左右的间距
layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10)
// 设置列数
layout.cols = 3
// 设置layout的数据源
layout.dataSource = self
let collectionView = UICollectionView(frame: CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height - 64), collectionViewLayout: layout)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.backgroundColor = UIColor.white
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellID)
return collectionView
}()
2、实现UICollectionViewDataSource数据源
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemCount
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellID, for: indexPath)
cell.backgroundColor = UIColor.randomColor()
var label: UILabel? = cell.contentView.subviews.first as? UILabel
if label == nil {
label = UILabel(frame: cell.bounds)
label?.textAlignment = .center
cell.contentView.addSubview(label!)
}
label?.text = "\(indexPath.item)"
return cell
}
3、 实现上拉加载更多
// 上拉加载更多
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y > scrollView.contentSize.height - scrollView.bounds.height {
itemCount += 30
collectionView.reloadData()
}
}
4、实现LGWaterfullLayoutDataSource数据源协议,返回cell的高度
func waterfullLayout(_ layout: UICollectionViewFlowLayout, itemIndex: Int) -> CGFloat {
return CGFloat(arc4random_uniform(150) + 100)
}