美文网首页
Swift实现UICollectionView支持多 secti

Swift实现UICollectionView支持多 secti

作者: 致青春_bf42 | 来源:发表于2020-07-17 16:05 被阅读0次
通过UICollectionViewLayout实现瀑布流

代码如下

@objc protocol WaterfallFlowLayoutDelegate: NSObjectProtocol {
    // 必选delegate实现
    /// collectionItem高度
    func heightForRowAtIndexPath(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, indexPath: IndexPath, itemWidth: CGFloat) -> CGFloat
    
    // 可选delegate实现
    /// 每个section 列数(默认2列)
    @objc optional func columnNumber(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, section: Int) -> Int
    
    /// header高度(默认为0)
    @objc optional func referenceSizeForHeader(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, section: Int) -> CGSize
    
    /// footer高度(默认为0)
    @objc optional func referenceSizeForFooter(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, section: Int) -> CGSize
    
    /// 每个section 边距(默认为0)
    @objc optional func insetForSection(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, section: Int) -> UIEdgeInsets
    
    /// 每个section item上下间距(默认为0)
    @objc optional func lineSpacing(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, section: Int) -> CGFloat
    
    /// 每个section item左右间距(默认为0)
    @objc optional func interitemSpacing(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, section: Int) -> CGFloat
    
    /// section头部header与上个section尾部footer间距(默认为0)
    @objc optional func spacingWithLastSection(collectionView collection: UICollectionView, layout: WaterfallFlowLayout, section: Int) -> CGFloat
}

class WaterfallFlowLayout: UICollectionViewLayout {
    
    weak var delegate: WaterfallFlowLayoutDelegate?
    
    private var sectionInsets: UIEdgeInsets = .zero
    private var columnCount: Int = 2
    private var lineSpacing: CGFloat = 0
    private var interitemSpacing: CGFloat = 0
    private var headerSize: CGSize = .zero
    private var footerSize: CGSize = .zero
    
    //存放attribute的数组
    private var attrsArray: [UICollectionViewLayoutAttributes] = []
    //存放每个section中各个列的最后一个高度
    private var columnHeights: [CGFloat] = []
    //collectionView的Content的高度
    private var contentHeight: CGFloat = 0
    //记录上个section高度最高一列的高度
    private var lastContentHeight: CGFloat = 0
    //每个section的header与上个section的footer距离
    private var spacingWithLastSection: CGFloat = 0
    
    
    override func prepare() {
        super.prepare()
        self.contentHeight = 0
        self.lastContentHeight = 0
        self.spacingWithLastSection = 0
        self.lineSpacing = 0
        self.sectionInsets = .zero
        self.headerSize = .zero
        self.footerSize = .zero
        self.columnHeights.removeAll()
        self.attrsArray.removeAll()
        
        let sectionCount = self.collectionView!.numberOfSections
        // 遍历section
        for idx in 0..<sectionCount {
            let indexPath = IndexPath(item: 0, section: idx)
            if let columnCount = self.delegate?.columnNumber?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
                self.columnCount = columnCount
            }
            if let inset = self.delegate?.insetForSection?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
                self.sectionInsets = inset
            }
            if let spacingLastSection = self.delegate?.spacingWithLastSection?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
                self.spacingWithLastSection = spacingLastSection
            }
            // 生成header
            let itemCount = self.collectionView!.numberOfItems(inSection: idx)
            let headerAttri = self.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: indexPath)
            if let header = headerAttri {
                self.attrsArray.append(header)
                self.columnHeights.removeAll()
            }
            self.lastContentHeight = self.contentHeight
            // 初始化区 y值
            for _ in 0..<self.columnCount {
                self.columnHeights.append(self.contentHeight)
            }
            // 多少个item
            for item in 0..<itemCount {
                let indexPat = IndexPath(item: item, section: idx)
                let attri = self.layoutAttributesForItem(at: indexPat)
                if let attri = attri {
                    self.attrsArray.append(attri)
                }
            }
            
            // 初始化footer
            let footerAttri = self.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionFooter, at: indexPath)
            if let footer = footerAttri {
                self.attrsArray.append(footer)
            }
        }
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return self.attrsArray
    }
    
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        if let column = self.delegate?.columnNumber?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
            self.columnCount = column
        }
        if let lineSpacing = self.delegate?.lineSpacing?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
            self.lineSpacing = lineSpacing
        }
        if let interitem = self.delegate?.interitemSpacing?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
            self.interitemSpacing = interitem
        }
        
        let attri = UICollectionViewLayoutAttributes(forCellWith: indexPath)
        let weight = self.collectionView!.frame.size.width
        let itemSpacing = CGFloat(self.columnCount - 1) * self.interitemSpacing
        let allWeight = weight - self.sectionInsets.left - self.sectionInsets.right - itemSpacing
        let cellWeight = allWeight / CGFloat(self.columnCount)
        let cellHeight: CGFloat = (self.delegate?.heightForRowAtIndexPath(collectionView: self.collectionView!, layout: self, indexPath: indexPath, itemWidth: cellWeight))!
        
        var tmpMinColumn = 0
        var minColumnHeight = self.columnHeights[0]
        for i in 0..<self.columnCount {
            let columnH = self.columnHeights[i]
            if minColumnHeight > columnH {
                minColumnHeight = columnH
                tmpMinColumn = i
            }
        }
        let cellX = self.sectionInsets.left + CGFloat(tmpMinColumn) * (cellWeight + self.interitemSpacing)
        var cellY: CGFloat = 0
        cellY = minColumnHeight
        if cellY != self.lastContentHeight {
            cellY += self.lineSpacing
        }
        
        if self.contentHeight < minColumnHeight {
            self.contentHeight = minColumnHeight
        }
        
        attri.frame = CGRect(x: cellX, y: cellY, width: cellWeight, height: cellHeight)
        self.columnHeights[tmpMinColumn] = attri.frame.maxY
        //取最大的
        for i in 0..<self.columnHeights.count {
            if self.contentHeight < self.columnHeights[i] {
                self.contentHeight = self.columnHeights[i]
            }
        }
        
        return attri
    }
    override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attri = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: elementKind, with: indexPath)
        if elementKind == UICollectionView.elementKindSectionHeader {
            if let headerSize = self.delegate?.referenceSizeForHeader?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
                self.headerSize = headerSize
            }
            self.contentHeight += self.spacingWithLastSection
            attri.frame = CGRect(x: 0, y: self.contentHeight, width: self.headerSize.width, height: self.headerSize.height)
            self.contentHeight += self.headerSize.height
            self.contentHeight += self.sectionInsets.top
        } else if elementKind == UICollectionView.elementKindSectionFooter {
            if let footerSize = self.delegate?.referenceSizeForFooter?(collectionView: self.collectionView!, layout: self, section: indexPath.section) {
                self.footerSize = footerSize
            }
            self.contentHeight += self.sectionInsets.bottom
            attri.frame = CGRect(x: 0, y: self.contentHeight, width: self.footerSize.width, height: self.footerSize.height)
            self.contentHeight += self.footerSize.height
        }
        return attri
    }
    
    override var collectionViewContentSize: CGSize {
        return CGSize(width: self.collectionView!.frame.size.width, height: self.contentHeight)
    }
    
}

调用如下

    let layout = WaterfallFlowLayout()
    layout.delegate = self
    let collection = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)

完整demo链接 瀑布流

相关文章

网友评论

      本文标题:Swift实现UICollectionView支持多 secti

      本文链接:https://www.haomeiwen.com/subject/ahvghktx.html