【问题标题】:How to align cell to top by Flow Layout in CollectionView如何通过 CollectionView 中的流布局将单元格与顶部对齐
【发布时间】:2018-08-09 21:23:54
【问题描述】:

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

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)
    }
}

我真正想要的是:

【问题讨论】:

  • 标准UICollectionViewFlowLayout 不支持您想要的布局。

标签: ios swift uicollectionview uicollectionviewlayout uicollectionviewflowlayout


【解决方案1】:

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

这里有一个很好的教程:

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

第 6 条我自己交出来的:

// 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
        }

【讨论】:

    【解决方案2】:

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

    说明

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

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

    这里是完整的代码

    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
        }
    }
    

    屏幕截图工作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-16
      • 2014-05-24
      • 2012-03-31
      • 1970-01-01
      • 2015-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多