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