iOS Swift 瀑布流

作者: 勇不言弃92 | 来源:发表于2018-01-25 17:11 被阅读25次

实现瀑布流布局的方式中利用“UICollectionView”与自定义“UICollectionViewLayout”的方式在内存利用、流畅度、实现难易度等方面都比较优秀
内存利用方面通过UICollectionView的cell复用可以有效的减少内存占用,流畅度也是表现的很出色,实现所需要的代码量也很少

WaterfallLayout

首先介绍一下自定义的布局方式,这个布局与“UICollectionViewFlowLayout”很相似,因此行间距、列间距、内边距都可以直接拿过来用,需要自己新定义这些属性。另外需要一些必须的属性,列数、属性数组,列高数组,为方便也要定义一下列宽(cell宽度)和代理(获取cell高度)属性,其中列宽与列间距、UICollectionView宽度、列数、内边距有关系,所以在这些值改变的时候记得在set方法中改变列宽。
在重写的“prepare”方法中初步设置列宽以及获取所有cell的布局属性。此时需要重写“func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?”,“func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes?”方法和“func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]?”方法,第一个方法就是在设置每一个cell的布局属性的时候,第二个方法是返回rect范围内所有元素的布局属性,包含cell、组视图等的布局属性.利用一个代理方法直接获取每个item的高度


屏幕快照 2018-01-25 18.03.25.png
import UIKit
protocol WaterfallLayoutDelegate {
    func itemHeightForIndexPath(indexpath : IndexPath) -> CGFloat?
}

class WaterfallLayout: UICollectionViewLayout {
    //行间距
    var minimumLineSpacing: CGFloat = 0.0{
        didSet{
            //设置item的宽度
            self.setUpItemWidth()
        }
    }
    
    //列间距
    var minimumInteritemSpacing: CGFloat = 0.0{
        didSet{
            //设置item的宽度
            self.setUpItemWidth()
        }
    }
    var scrollDirection: UICollectionViewScrollDirection = .vertical // default is UICollectionViewScrollDirectionVertical
    fileprivate var item_w : CGFloat = 0//item宽度
    //内边距
    var sectionInset: UIEdgeInsets = UIEdgeInsets.zero{
        didSet{
            //设置item的宽度
            self.setUpItemWidth()
        }
    }
    //列数,默认2
    var columnsNum = 2{
        didSet{
            //设置列高
            self.columnsHeightArray.removeAll()
            for _ in 0...self.columnsNum{
                //如果数量不对则全部设置为0
                self.columnsHeightArray.append(0)
            }
            //设置item的宽度
            self.setUpItemWidth()
        }
    }
    
    var delegate : WaterfallLayoutDelegate?
    
    fileprivate var attrArray : Array<UICollectionViewLayoutAttributes> = Array<UICollectionViewLayoutAttributes>()//属性数组
    fileprivate var columnsHeightArray : Array<CGFloat> = [0,0]//每列的高度

    //设置每一个item的属性
    func setAttrs() {
        guard let secNum = self.collectionView?.numberOfSections else {
            return
        }
        for i in 0...secNum-1{
            for i in 0...self.columnsNum - 1{
                self.columnsHeightArray[i] = self.getLongValue()
            }
            self.attrArray.append(self.layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: IndexPath.init(row: 0, section: i))!)
            guard let itemsNum = self.collectionView?.numberOfItems(inSection: i) else {
                return
            }
            for j in 0...itemsNum - 1{
                self.attrArray.append(self.layoutAttributesForItem(at: IndexPath.init(row: j, section: i))!)
            }
        }
    }
    
    //获取最短列的索引
    func getShortesIndex() -> Int {
        var index = 0
        for i in 0...self.columnsNum - 1{
            if self.columnsHeightArray[index] > self.columnsHeightArray[I]{
                index = I
            }
        }
        return index
    }
    
    //获取最长列的值
    func getLongValue() -> CGFloat {
        var value : CGFloat = 0
        for i in 0...self.columnsNum - 1{
            if value < self.columnsHeightArray[I]{
                value = self.columnsHeightArray[I]
            }
        }
        return value
    }
    
    //设置每列的宽度
    func setUpItemWidth(){
        guard let collectionWidth = self.collectionView?.frame.size.width else {
            return
        }
        self.item_w = (collectionWidth - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing * CGFloat((self.columnsNum - 1))) / CGFloat(self.columnsNum)
    }
    
    override var collectionViewContentSize: CGSize{
        get{
            return CGSize.init(width: 0, height: self.getLongValue() + self.sectionInset.top + self.sectionInset.bottom)
        }
    }
    
    override func prepare() {
        super.prepare()
        self.setUpItemWidth()
        self.setAttrs()
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attr = UICollectionViewLayoutAttributes.init(forCellWith: indexPath)
        let shortesIndex = self.getShortesIndex()
        let item_x = self.sectionInset.left + (self.item_w + self.minimumInteritemSpacing) * CGFloat(shortesIndex)
        let item_y = self.columnsHeightArray[shortesIndex] + self.sectionInset.top
        let item_h = self.delegate?.itemHeightForIndexPath(indexpath: indexPath) ?? 0
        attr.frame = CGRect.init(x: item_x, y: item_y , width: self.item_w, height: item_h)
        
        //更新列高数组
        self.columnsHeightArray[shortesIndex] += (item_h + self.minimumLineSpacing)
        return attr
    }
        override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attr = UICollectionViewLayoutAttributes.init(forSupplementaryViewOfKind: elementKind, with: indexPath)
        let header_x : CGFloat = self.sectionInset.left
        let header_y : CGFloat = self.columnsHeightArray[0] + self.minimumLineSpacing + self.sectionInset.top
        let header_w = self.item_w * CGFloat(self.columnsNum) + self.minimumInteritemSpacing * CGFloat(self.columnsNum - 1)
        let header_h : CGFloat = 40
        attr.frame = CGRect.init(x: header_x, y: header_y, width: header_w, height: header_h)
        for i in 0...self.columnsNum - 1{
            self.columnsHeightArray[i] += (header_h + self.minimumLineSpacing)
        }
        return attr
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return self.attrArray
    }
}

WaterfallViewController

就是普通的collectionview的实现

//
//  WaterfallViewController.swift
//  qixiaofu
//
//  Created by ly on 2018/1/24.
//  Copyright © 2018年 qixiaofu. All rights reserved.
//

import UIKit

private let reuseIdentifier = "Cell"

class WaterfallViewController: UIViewController {
   
   fileprivate var collectionView : UICollectionView!
   
   override func viewDidLoad() {
       super.viewDidLoad()
       
       
       self.setUpCollectionView()
       
   }
   
   func setUpCollectionView() {
       let layout = WaterfallLayout()
       layout.delegate = self
       layout.sectionInset = UIEdgeInsets.init(top: 10, left: 10, bottom: 10, right: 10)
       layout.minimumInteritemSpacing = 10
       layout.minimumLineSpacing = 5
       self.collectionView = UICollectionView.init(frame: self.view.bounds, collectionViewLayout: layout)
       self.collectionView.delegate = self
       self.collectionView.dataSource = self
       self.view.addSubview(self.collectionView)
       self.collectionView.backgroundColor = UIColor.blue
       self.collectionView.register(UINib.init(nibName: "WaterfallCell", bundle: Bundle.main), forCellWithReuseIdentifier: "WaterfallCell")
       self.collectionView.register(WaterfallReusableView.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "WaterfallReusableView")
   }
   
   override func didReceiveMemoryWarning() {
       super.didReceiveMemoryWarning()
       
   }
   
}

extension WaterfallViewController : UICollectionViewDelegate, UICollectionViewDataSource{
   // MARK: UICollectionViewDataSource
   func numberOfSections(in collectionView: UICollectionView) -> Int {
       return 3
   }
   
   func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
       return 10
   }
   
   func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
       let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WaterfallCell", for: indexPath)
       
       cell.backgroundColor = UIColor.red
       
       // Configure the cell
       
       return cell
   }
   
   func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
       let reusedView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "WaterfallReusableView", for: indexPath)
       reusedView.backgroundColor = UIColor.green
       
       return reusedView
   }
}

extension WaterfallViewController : WaterfallLayoutDelegate{
   func itemHeightForIndexPath(indexpath: IndexPath) -> CGFloat? {
       return CGFloat(arc4random() % UInt32(100)) + 10.0
   }
}


class WaterfallReusableView: UICollectionReusableView {
   
}

相关文章

网友评论

    本文标题:iOS Swift 瀑布流

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