如何在 CollectionView 中通过 Flow Layout 将单元格对齐到顶部 [英] How to alig cell to top by Flow Layout in CollectionView

查看:35
本文介绍了如何在 CollectionView 中通过 Flow Layout 将单元格对齐到顶部的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这段代码中,我试图更改 UICollectionView 的第一个单元格的大小和其他具有相同大小的单元格,但在第一行中,当我想要两个单元格出现时,只有一个单元格出现:

In this code I am trying to change the size of the first cell of the UICollectionView and the others with the same size but in the first row only one cell is coming out when I want two to come out:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout:
    if indexPath.row == 0 {
        return CGSize(width: collectionView.frame.width/1.5-2, height: collectionView.frame.width/1.5-2)
    }
    else {
        return CGSize(width: collectionView.frame.width/3.0-3, height: collectionView.frame.width/3.0-3)
    }
}

我真正想要的是:

推荐答案

你需要实现一个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 中通过 Flow Layout 将单元格对齐到顶部的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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