image.png
大神解说原理是一样的这里主要是Swift版的
1.定义所需属性
瀑布流的思路就是,从上往下,那一列最短,就把下一个item放在哪一列,因此我们需要定义一个字典来记录每一列的最大y值
每一个item都有一个attributes,因此定义一个数组来保存每一个item的attributes。
我们还必须知道有多少列以及列间距、行间距、section到collectionView的边距。
image.png
2.重写系统方法
我们一共需要重写4个方法
1)- (void)prepareLayout
2)- (CGSize)collectionViewContentSize
3)- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
4)- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
- (void)prepareLayout 方法
布局前的一些准备工作都在这里进行,初始化字典,有几列就有几个键值对,key为第几列,value为列的最大y值,初始值为上内边距:
接下来计算item的坐标,要想计算坐标,那就必须知道最短的那一列,先遍历字典,找出最短列是哪一列(minColumn)以及其最大y值。
item的y值就等于最短列的最大y值再加上行间距,x值就等于左边距 + (item宽度 + 列间距) * minColumn
image.png
直接上代码 (每一步都有注释)
`
}
/// 准备所有view的layoutAttribute信息
override func prepare() {
super.prepare()
if self.collectionView == nil { return }
/// 清除列高度数组
self.columnHeights.removeAll()
/// 清除内容高度
self.contentHeight = 0
/// 把每组的额外滚动区域高度添加进去
for _ in 0 ..< self.columnCount() {
self.columnHeights.append(self.edgeInsets().top)
}
/// 清除属性
self.attrs.removeAll()
/// 获取组数
let count : Int = (self.collectionView?.numberOfItems(inSection: 0))!
/// 获取宽度
let width : CGFloat = (self.collectionView?.fra``````
let colMagin = (CGFloat)(self.columnCount() - 1) * self.columnMargin()
/// 计算cell宽度
let cellWidth : CGFloat = (width - self.edgeInsets().left - self.edgeInsets().right - colMagin) / CGFloat(self.columnCount())
for i in 0 ..< count {
let indexPath : NSIndexPath = NSIndexPath(item: i, section: 0)
let attr : UICollectionViewLayoutAttributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath)
//获取高度
let cellHeight : CGFloat = (self.delegate?.hw_setCellHeght(layout: self, indexPath: indexPath as NSIndexPath, itemWidth: cellWidth))!
/// 默认最小高度为第一组
var minColumnHeight = self.columnHeights[0]
var minColumn : Int = 0
/// 筛选最小高度的组
for i in 1 ..< self.columnCount() {
let colHeight = self.columnHeights[i]
if colHeight < minColumnHeight {
minColumnHeight = colHeight
minColumn = i
}
}
/// 将下一个cell添加在最小高度的那一组
let cellX : CGFloat = self.edgeInsets().left + CGFloat(minColumn) * (self.columnMargin() + cellWidth)
var cellY = minColumnHeight
if cellY != self.edgeInsets().top { // 如果不是第一次加入需要加上行间距
cellY = self.rowMargin() + cellY
}
/// 设置frame
attr.frame = CGRect(x: cellX, y: cellY, width: cellWidth, height: cellHeight)
/// 那么当前的最大Y值就是新加的这个Cell的底部
let maxY = cellY + cellHeight;
/// 修改该组的列高度
self.columnHeights[minColumn] = maxY
/// 比较当前的最大高度是否是大于内容高度的
let maxContentHeight = self.columnHeights[minColumn]
if CGFloat(self.contentHeight!) < CGFloat(maxContentHeight) {
/// 大于修改内容高度
self.contentHeight = maxContentHeight
}
/// 添加属性数组
self.attrs.append(attr)
}
}
/// 返回属性数组
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return self.attrs
}
/// 返回内容高度
override var collectionViewContentSize: CGSize {
get {
return CGSize(width: 0, height: CGFloat(self.contentHeight!) + CGFloat(self.edgeInsets().bottom))
}
set {
self.collectionViewContentSize = newValue
}
}
}