- 效果演示
簡(jiǎn)單易用垄惧,注釋清晰拆内,調(diào)用方便栏尚,歡迎共同交流渤刃,一起進(jìn)步
實(shí)現(xiàn)思路
用collectionView實(shí)現(xiàn)各種吊炸天的布局拥峦,其精髓就在于UICollectionViewLayout,因此我們要自定義一個(gè)layout來(lái)繼承系統(tǒng)的UICollectionViewLayout卖子,所有工作都在這個(gè)類中進(jìn)行略号。
核心代碼
- 重寫4個(gè)系統(tǒng)方法
第一個(gè)方法
override func prepare()
第二方法
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
第三個(gè)方法
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
第四個(gè)方法
override var collectionViewContentSize: CGSize
2.布局思路
瀑布流的思路就是,從上往下,那一列最短玄柠,就把下一個(gè)item放在哪一列突梦,因此我們需要定義一個(gè)字典來(lái)記錄每一列的最大y值
每一個(gè)item都有一個(gè)attributes,因此定義一個(gè)數(shù)組來(lái)保存每一個(gè)item的attributes羽利。
我們還必須知道有多少列以及列間距宫患、行間距、section到collectionView的邊距铐伴。
fileprivate let defaultColumnCout : Int = 3 //默認(rèn)的列數(shù)
fileprivate let defaultColumnMargin : CGFloat = 10 //默認(rèn)每一縱列的間距
fileprivate let defaultRowMargin : CGFloat = 10 //默認(rèn)每一行的間距
fileprivate let defaultEdgInset : UIEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) //默認(rèn)邊緣間距的大小`
- override func prepare() 方法
布局前的一些準(zhǔn)備工作都在這里進(jìn)行撮奏,初始化字典,有幾列就有幾個(gè)鍵值對(duì)当宴,key為第幾列畜吊,value為列的最大y值,初始值為上內(nèi)邊距:
//初始化 首次布局和更新布局都會(huì)調(diào)用
override func prepare() {
//清除緩存數(shù)據(jù)
attrArray.removeAllObjects()
heightArray.removeAll()
//初始化高度數(shù)據(jù)
for _ in 0..<self.columnCount {
heightArray.append(self.collEdgInset.top)
}
let count = self.collectionView?.numberOfItems(inSection: 0)
for i in 0..<count! {
//獲取每個(gè)item
let index = IndexPath(item: i, section: 0)
//設(shè)置item的屬性
let attr = layoutAttributesForItem(at: index)
attrArray.add(attr!)
}
}
- override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? 方法
該方法則用來(lái)設(shè)置每個(gè)item的attributes户矢,在這里玲献,我們只需要簡(jiǎn)單的設(shè)置每個(gè)item的attributes.frame即可
首先我們必須得知collectionView的尺寸,然后我們根據(jù)collectionView的寬度梯浪,以及列數(shù)捌年、各個(gè)間距來(lái)計(jì)算每個(gè)item的寬度
//設(shè)置每個(gè)item的布局屬性
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
//設(shè)置位置坐標(biāo)
let collW = self.collectionView?.bounds.size.width
let w = (collW! - collEdgInset.left - collEdgInset.right - (CGFloat(columnCount - 1) * columnMargin)) / CGFloat(columnCount)
var itemH : CGFloat = 100
if let h = delegate?.itemHeightInBigCatflowLayout(self, indexPath: indexPath, itemWidth: w) {
itemH = h
}
//找出高度最短的那一列
var minHeight = heightArray[0]
var minIdex = 0
//找出最短的那列并記錄更新
for i in 1..<columnCount{
if minHeight > heightArray[i] {
minHeight = heightArray[i]
minIdex = i
}
}
let x = collEdgInset.left + CGFloat(minIdex) * ( w + columnMargin)
var y = minHeight
if y != collEdgInset.top { //不是第一行的時(shí)候
y = minHeight + rowMargin
}
heightArray[minIdex] = y + itemH //更新最短高度
attr.frame = CGRect(x: x, y: y, width: w, height: itemH)
return attr
}
- 剩余的方法
//返回所有的布局屬性
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return attrArray as? [UICollectionViewLayoutAttributes]
}
//返回 collectionViewContentSize
override var collectionViewContentSize: CGSize {
//找出高度最高的那一列
var maxHeight = heightArray[0]
for i in 1..<columnCount{
if maxHeight < heightArray[i] {
maxHeight = heightArray[i]
}
}
return CGSize(width: 0 , height: maxHeight)
}
通過代理可以在外部直接修改一些默認(rèn)的屬性,計(jì)算高度挂洛,所有的數(shù)據(jù)操作在類外部進(jìn)行即可
@objc protocol BigCatLayoutDelegate {
func itemHeightInBigCatflowLayout(_ bigCatLayout : BigCatLayout , indexPath : IndexPath , itemWidth : CGFloat) -> CGFloat
@objc optional func colunCount() -> Int
@objc optional func columnMargin() -> CGFloat
@objc optional func rowMargin() -> CGFloat
@objc optional func collEdgInset() -> UIEdgeInsets
}