自定义UICollectionViewLayout 实现标签的功能

和上一篇自定义的瀑布流同样,自定义瀑布流也是须要实现那个几个方法bash

// 生成每一个视图的布局属性(头尾视图和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?
复制代码

最关键的地方是在markdown

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?
复制代码

这个方法, 须要判断这个 item 的 width + x 是否超过了最大的宽度, 根据这个要换行app

具体的代码以下ide

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
    }
}

复制代码
相关文章
相关标签/搜索