自定義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
    }
}

複製代碼
相關文章
相關標籤/搜索