首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在CollectionView中通过流布局将单元格对齐

如何在CollectionView中通过流布局将单元格对齐
EN

Stack Overflow用户
提问于 2018-03-01 18:01:30
回答 2查看 1.5K关注 0票数 3

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

代码语言:javascript
复制
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)
    }
}

我真正想要的是:

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-03-02 09:22:08

您需要实现一个FillingLayout,,我称它为UICollectionViewLayout注意,您可以使用delegate方法来调整列数和大单元格的大小。

解释

您需要添加一个数组来跟踪您的列的高度,并查看什么是最短的列,即private var columsHeights : [CGFloat] = [];您还需要一个(Int,Float)元组数组来保留要填充的空格,我还在委托中添加了一个方法,以获取我们在集合视图中想要的列数,还需要一个方法来了解是否可以根据单元格的大小添加一个单元格。

然后,如果我们想要添加一个单元格,我们检查是否可以添加,因为第一列是填充的,我们在avaiableSpaces数组中添加对应于avaiableSpaces的空间,当我们首先添加下一个单元格时,我们检查是否可以在任何可用的空间中添加,如果可以的话,我们可以添加和删除可用的空间。

这里是完整的代码

代码语言:javascript
复制
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

代码语言:javascript
复制
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
    }

}

FillingLayoutDelegate在ViewController中的实现

代码语言:javascript
复制
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
    }
}

ScreenShot working

票数 1
EN

Stack Overflow用户

发布于 2018-03-01 18:07:47

这不是你问题的确切答案,但也许它会对你有帮助。

这里有一个非常好的教程:

https://www.raywenderlich.com/164608/uicollectionview-custom-layout-tutorial-pinterest-2

我交给自己的第六项:

代码语言:javascript
复制
// 6. Updates the collection view content height
        contentHeight = max(contentHeight, frame.maxY)
        yOffset[column] = yOffset[column] + height

        //Only for 2 columns
        if yOffset[1] >= yOffset[0] {
            column = 0
        } else {
            column = 1
        }
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49055966

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档