2

I am trying to create a UICollectionView with cells that have varying widths but constant heights. The UICollectionView has 3 rows and scrolls horizontally.

Each cell should have 8px between the next cell to it's right and the row below. I have set this as the minimum spacing in IB.

When I have the scrolling set to vertical the following code works, all cells get aligned to left and the spacing is 8px between all cells. However if this code is used when scrolling is horizontal, this does not work.

Any direction would be greatly appreciated.

class HorizontalCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override init(){
        super.init()
        scrollDirection = .horizontal
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        self.scrollDirection = .horizontal
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)

        var leftMargin = sectionInset.left
        var maxY: CGFloat = -1.0
        attributes?.forEach { layoutAttribute in
            if layoutAttribute.frame.origin.y >= maxY {
                leftMargin = sectionInset.left
            }

            layoutAttribute.frame.origin.x = leftMargin

            leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
            maxY = max(layoutAttribute.frame.maxY , maxY)
        }

        return attributes
    }

}

Edit: This is the UI that I am trying to achieve.

enter image description here

Josh
  • 254
  • 3
  • 19

1 Answers1

0

This code works for me for scrollDirection = .horizontal

 import UIKit

class LeftAlignedHorizontalCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
    required override init() {super.init(); common()}
        required init?(coder aDecoder: NSCoder) {super.init(coder: aDecoder); common()}
        
        private func common() {
            scrollDirection = .horizontal
            estimatedItemSize = UICollectionViewFlowLayout.automaticSize
            minimumLineSpacing = 10
            minimumInteritemSpacing = 8
        }
        
        override func layoutAttributesForElements(
                        in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            
            guard let att = super.layoutAttributesForElements(in:rect) else {return []}
            
            let group = att.group(by: {$0.frame.origin.y})
            
            var x: CGFloat = sectionInset.left
            
            for attr in group {
                x = sectionInset.left
                for a in attr.value {
                    if a.representedElementCategory != .cell { continue }
                    a.frame.origin.x = x
                    x += a.frame.width + minimumInteritemSpacing
                }
            }
            return att
        }
}

extension Array {
    func group<T: Hashable>(by key: (_ element: Element) -> T) -> [[Element]] {
        var categories: [T: [Element]] = [:]
        var groups = [[Element]]()
        for element in self {
            let key = key(element)
            if case nil = categories[key]?.append(element) {
                categories[key] = [element]
            }
        }
        categories.keys.forEach { key in
            if let group = categories[key] {
                groups.append(group)
            }
        }
        return groups
    }
}
Sargis
  • 1,196
  • 1
  • 18
  • 31