CollectionView Flowlayout 自定义 [英] CollectionView Flowlayout Customize

查看:30
本文介绍了CollectionView Flowlayout 自定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作个人资料图片收藏视图,例如 Tinder 编辑个人资料图片.我希望第一个单元格比其他单元格大,除了第一个单元格之外还有 2、3 个单元格,其他单元格应该像 3、4、5.有什么建议吗?

I am making a profile picture collectionview like tinder edit profile pictures. I want first cell bigger than others and 2, 3 cells besides first cell and others should like 3, 4, 5. Any suggestion?

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        if indexPath.item == 0 {
        return CGSize(width: 213.34, height: 213.34)
        } else {
            return CGSize(width: 101.66, height:101.66 )
        }

    }


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 6
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
        let lbl = cell.viewWithTag(1) as! UILabel
        lbl.text = String(format: "%d", indexPath.row + 1)
        return cell
    }


}

推荐答案

你需要实现一个UICollectionViewLayout,我之前叫它FillingLayout,注意你可以调整delegate 方法

You need to implement an UICollectionViewLayout, I had called it FillingLayout, Note that you can adjust the number of columns and the size of your big cells with the delegate methods

说明

您需要添加一个数组来跟踪列的高度并查看最短的列是 private var columsHeights : [CGFloat] = [] 并且您还需要一个 的数组(Int,Float) 元组来保持哪些空间可以填充,我还在委托中添加了一个方法来获取我们想要的集合View中的列数和一个方法来知道一个单元格是否可以填充根据它们的大小在某个位置添加或不添加.

You need to add an Array to track your columns heigths and see what is the shortest column that is private var columsHeights : [CGFloat] = [] and you need also an array of (Int,Float) tuple to keep which spaces are available to be filled, I also added a method in the delegate to get the number of columns we want in the collection View and a method to know if a cell can be added or not in a position according their size.

然后如果我们要添加一个单元格我们检查是否可以添加如果不可以,因为第一列被填满了我们在avaiableSpaces数组中添加了column2对应的空间,当我们首先添加下一个单元格时我们检查是否可以添加到任何可用空间如果可以添加我们添加和删除可用空间.

Then if we want to add a cell we check if can be added if not, because the first column is filled we add the space corresponding to column2 in the avaiableSpaces array and when we add the next cell first we check if can be added in any available space if can be added we add and remove the available space.

这里是完整的代码

import UIKit

protocol FillingLayoutDelegate: class {
    func collectionView(_ collectionView:UICollectionView, sizeForViewAtIndexPath indexPath:IndexPath) -> Int
    //  Returns the amount of columns that have to display at that moment
    func numberOfColumnsInCollectionView(collectionView:UICollectionView) ->Int
}

class FillingLayout: UICollectionViewLayout {
    weak var delegate: FillingLayoutDelegate!

    fileprivate var cellPadding: CGFloat = 10

    fileprivate var cache = [UICollectionViewLayoutAttributes]()

    fileprivate var contentHeight: CGFloat = 0
    private var columsHeights : [CGFloat] = []
    private var avaiableSpaces : [(Int,CGFloat)] = []

    fileprivate var contentWidth: CGFloat {
        guard let collectionView = collectionView else {
            return 0
        }
        let insets = collectionView.contentInset
        return collectionView.bounds.width - (insets.left + insets.right)
    }

    var columnsQuantity : Int{
        get{
            if(self.delegate != nil)
            {
                return (self.delegate?.numberOfColumnsInCollectionView(collectionView: self.collectionView!))!
            }
            return 0
        }
    }

    //MARK: PRIVATE METHODS
    private func shortestColumnIndex() -> Int{
        var retVal : Int = 0
        var shortestValue = MAXFLOAT

        var i = 0
        for columnHeight in columsHeights {
            //debugPrint("Column Height: \(columnHeight) index: \(i)")
            if(Float(columnHeight) < shortestValue)
            {
                shortestValue = Float(columnHeight)
                retVal = i
            }
            i += 1
        }
        //debugPrint("shortest Column index: \(retVal)")
        return retVal
    }

    //MARK: PRIVATE METHODS
    private func largestColumnIndex() -> Int{
        var retVal : Int = 0
        var largestValue : Float = 0.0

        var i = 0
        for columnHeight in columsHeights {
            //debugPrint("Column Height: \(columnHeight) index: \(i)")
            if(Float(columnHeight) > largestValue)
            {
                largestValue = Float(columnHeight)
                retVal = i
            }
            i += 1
        }
        //debugPrint("shortest Column index: \(retVal)")
        return retVal
    }

    private func canUseBigColumnOnIndex(columnIndex:Int,size:Int) ->Bool
    {
        if(columnIndex < self.columnsQuantity - (size-1))
        {
            let firstColumnHeight = columsHeights[columnIndex]
            for i in columnIndex..<columnIndex + size{
                if(firstColumnHeight != columsHeights[i]) {
                    return false
                }
            }
            return true
        }

        return false
    }

    override var collectionViewContentSize: CGSize {
        return CGSize(width: contentWidth, height: contentHeight)
    }

    override func prepare() {
        // Check if cache is empty
        guard cache.isEmpty == true, let collectionView = collectionView else {
            return
        }

        //  Set all column heights to 0
        self.columsHeights = []
        for _ in 0..<self.columnsQuantity {
            self.columsHeights.append(0)
        }

        for item in 0 ..< collectionView.numberOfItems(inSection: 0) {

            let indexPath = IndexPath(item: item, section: 0)

            let viewSize: Int = delegate.collectionView(collectionView, sizeForViewAtIndexPath: indexPath)
            let blockWidth = (contentWidth/CGFloat(columnsQuantity))
            let width = blockWidth * CGFloat(viewSize)
            let height = width

            var columIndex = self.shortestColumnIndex()
            var xOffset = (contentWidth/CGFloat(columnsQuantity)) * CGFloat(columIndex)
            var yOffset = self.columsHeights[columIndex]

            if(viewSize > 1){//Big Cell
                if(!self.canUseBigColumnOnIndex(columnIndex: columIndex,size: viewSize)){
                    //  Set column height
                    for i in columIndex..<columIndex + viewSize{
                        if(i < columnsQuantity){
                            self.avaiableSpaces.append((i,yOffset))
                            self.columsHeights[i] += blockWidth
                        }
                    }
                    //  Set column height
                    yOffset = columsHeights[largestColumnIndex()]
                    xOffset = 0
                    columIndex = 0
                }

                for i in columIndex..<columIndex + viewSize{
                    if(i < columnsQuantity){
                        //current height
                        let currValue = self.columsHeights[i]
                        //new column height with the update
                        let newValue = yOffset + height
                        //space that will remaing in blank, this must be 0 if its ok
                        let remainder = (newValue - currValue) - CGFloat(viewSize) * blockWidth
                        if(remainder > 0) {
                            debugPrint("Its bigger remainder is \(remainder)")
                            //number of spaces to fill
                            let spacesTofillInColumn = Int(remainder/blockWidth)
                            //we need to add those spaces as avaiableSpaces
                            for j in 0..<spacesTofillInColumn {
                                self.avaiableSpaces.append((i,currValue + (CGFloat(j)*blockWidth)))
                            }
                        }
                        self.columsHeights[i] = yOffset + height
                    }
                }
            }else{
                //if there is not avaiable space
                if(self.avaiableSpaces.count == 0)
                {
                    //  Set column height
                    self.columsHeights[columIndex] += height
                }else{//if there is some avaiable space
                    yOffset = self.avaiableSpaces.first!.1
                    xOffset = CGFloat(self.avaiableSpaces.first!.0) * width
                    self.avaiableSpaces.remove(at: 0)
                }
            }

            print(width)

            let frame = CGRect(x: xOffset, y: yOffset, width: width, height: height)
            let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)

            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = insetFrame
            cache.append(attributes)

            contentHeight = max(contentHeight, frame.maxY)
        }
    }

    func getNextCellSize(currentCell: Int, collectionView: UICollectionView) -> Int {
        var nextViewSize = 0
        if currentCell < (collectionView.numberOfItems(inSection: 0) - 1) {
            nextViewSize = delegate.collectionView(collectionView, sizeForViewAtIndexPath: IndexPath(item: currentCell + 1, section: 0))
        }
        return nextViewSize
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

        var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()

        // Loop through the cache and look for items in the rect
        for attributes in cache {
            if attributes.frame.intersects(rect) {
                visibleLayoutAttributes.append(attributes)
            }
        }
        return visibleLayoutAttributes
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return cache[indexPath.item]
    }
}

更新

您需要将 viewController 设置为 FillingLayoutDelegate

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView.delegate = self
    collectionView.dataSource = self

    // Do any additional setup after loading the view.
    if let layout = self.collectionView.collectionViewLayout as? FillingLayout
    {
        layout.delegate = self
    }

}

ViewController 中的 FillingLayoutDelegate 实现

extension ViewController: FillingLayoutDelegate{
func collectionView(_ collectionView:UICollectionView,sizeForViewAtIndexPath indexPath:IndexPath) ->Int{
        if(indexPath.row == 0 || indexPath.row == 4)
        {
            return 2
        }

        if(indexPath.row == 5)
        {
            return 3
        }

        return 1
    }

    func numberOfColumnsInCollectionView(collectionView:UICollectionView) ->Int{
        return 3
    }
}

屏幕截图工作

这篇关于CollectionView Flowlayout 自定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆