和上一篇自定义的瀑布流一样,自定义瀑布流也是需要实现那个几个方法
// 生成每个视图的布局属性(头尾视图和cell的布局属性)
override func prepare()
// 返回滚动区域的大小,当你的UICollectionView 不滚动的情况下可以检查这个方法
override var collectionViewContentSize: CGSize{}
// 返回该区域内的布局属性
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?
// 返回 indexpath 位置上的 cell 对应的布局属性
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
最关键的地方是在
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
这个方法, 需要判断这个 item 的 width + x 是否超过了最大的宽度, 根据这个要换行
具体的代码如下
import UIKit
protocol UICollectionViewCategoryLayoutDelegate {
// require
/// 返回每个item的Size
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, sizeForItemAt indexPath:IndexPath) -> CGSize
// optional
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, columnMarginAt indexPath:IndexPath) -> CGFloat
/// 行间距
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, rowMarginAt indexPath:IndexPath) -> CGFloat
/// 列间距
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, edgInsetAt indexPath:IndexPath) -> UIEdgeInsets
/// head的frame
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, sizeForHeaderViewIn section:Int) -> CGSize
/// foot的frame
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, sizeForFooterViewIn section:Int) -> CGSize
}
extension UICollectionViewCategoryLayoutDelegate{
/// 返回列间距 默认是 10
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, columnMarginAt indexPath:IndexPath) -> CGFloat {
return 10
}
/// 返回行间距 默认是10
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, rowMarginAt indexPath:IndexPath) -> CGFloat{
return 10
}
/// 返回每个 section 的 UIEdgeInsets
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, edgInsetAt indexPath:IndexPath) -> UIEdgeInsets{
return UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
}
/// 返回透视图的size
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, sizeForHeaderViewIn section:Int) -> CGSize{
return .zero
}
/// 返回尾视图的size
func categoryLayout(_ categoryLayout:UICollectionViewCategoryLayout, sizeForFooterViewIn section:Int) -> CGSize{
return .zero
}
}
class UICollectionViewCategoryLayout: UICollectionViewLayout {
/// 存储最大的高度
var maxHeight:CGFloat = 0.0
var deleagte:UICollectionViewCategoryLayoutDelegate!
/// 存放当前 Rect 中的所有 布局属性
lazy var attriArray:[UICollectionViewLayoutAttributes] = []
/// 记录上个cell的 最大 X 值 x + width
var lastMaxX:CGFloat = 0.0
/// 上个cell的 Y值
var lastMaxY:CGFloat = 0.0
override func prepare() {
super.prepare()
self.attriArray.removeAll()
self.maxHeight = 0.0
self.lastMaxX = 0.0
self.lastMaxY = 0.0
if let collectionView = self.collectionView {
let sectionCount = collectionView.numberOfSections
for section in 0..<sectionCount{
self.lastMaxX = 0.0
// header
let headerAttr = self.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: IndexPath.init(item: 0, section: section))
if let headerAttr = headerAttr {
self.attriArray.append(headerAttr)
}
// item
let rowCount = collectionView.numberOfItems(inSection: section)
for item in 0..<rowCount{
let itemAttri = self.layoutAttributesForItem(at: IndexPath.init(item: item, section: section))
if let itemAttri = itemAttri{
self.attriArray.append(itemAttri)
}
}
// footer
let footerAttri = self.layoutAttributesForSupplementaryView(ofKind:UICollectionView.elementKindSectionFooter, at: IndexPath.init(item: 0, section: section))
if let footerAttri = footerAttri {
self.attriArray.append(footerAttri)
}
}
}
}
override var collectionViewContentSize: CGSize{
return CGSize(width: 0, height: self.maxHeight)
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return self.attriArray
}
override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
var attri:UICollectionViewLayoutAttributes!
if elementKind == UICollectionView.elementKindSectionHeader {
attri = UICollectionViewLayoutAttributes.init(forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, with: indexPath)
attri.frame = self.headerViewFrame(indexPath: indexPath)
}else{
attri = UICollectionViewLayoutAttributes.init(forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, with: indexPath)
attri.frame = self.footerViewFrame(indexPath: indexPath)
}
return attri
}
func headerViewFrame(indexPath:IndexPath) -> CGRect {
let edgInset = self.deleagte.categoryLayout(self, edgInsetAt: indexPath)
let size = self.deleagte.categoryLayout(self, sizeForHeaderViewIn: indexPath.section)
let rwoMargin = self.deleagte.categoryLayout(self, rowMarginAt: indexPath)
let x:CGFloat = 0.0
let y = self.maxHeight == 0.0 ? edgInset.top : self.maxHeight
self.maxHeight = self.maxHeight + size.height + rwoMargin
self.lastMaxY = self.maxHeight
return CGRect(x: x, y: y, width: size.width, height: size.height)
}
func footerViewFrame(indexPath:IndexPath) -> CGRect {
let edgInset = self.deleagte.categoryLayout(self, edgInsetAt: indexPath)
let size = self.deleagte.categoryLayout(self, sizeForFooterViewIn: indexPath.section)
let rwoMargin = self.deleagte.categoryLayout(self, rowMarginAt: indexPath)
let x:CGFloat = 0.0
let y = self.maxHeight == 0.0 ? edgInset.top : self.maxHeight + rwoMargin
self.maxHeight = self.maxHeight + size.height
self.lastMaxY = self.maxHeight
return CGRect(x: x, y: y, width: size.width, height: size.height)
}
override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
let attr = UICollectionViewLayoutAttributes.init(forCellWith: indexPath)
let width = self.collectionView!.frame.size.width
let edgInset = self.deleagte.categoryLayout(self, edgInsetAt: indexPath)
let size = self.deleagte.categoryLayout(self, sizeForItemAt: indexPath)
let columMarigin = self.deleagte.categoryLayout(self, columnMarginAt: indexPath)
let rowMargin = self.deleagte.categoryLayout(self, rowMarginAt: indexPath)
var x:CGFloat = 0.0
if self.maxHeight == 0.0 {
self.lastMaxY = edgInset.top
}
// 表示行的最开始
if self.lastMaxX >= edgInset.left {
x = self.lastMaxX + columMarigin
self.lastMaxX = x + size.width
// 换行显示了
if x + size.width + edgInset.right > width {
x = edgInset.left
// 换行
self.lastMaxX = edgInset.left + size.width
self.lastMaxY = self.lastMaxY + size.height + rowMargin
}
}else{
// 有可能存在一个 标签太长, 一整行都显示不下去
x = edgInset.left
self.lastMaxX = edgInset.left + size.width
}
self.maxHeight = self.lastMaxY + size.height + edgInset.bottom
attr.frame = CGRect(x: x, y: self.lastMaxY, width: size.width, height: size.height)
return attr
}
}