This code works for me for scrollDirection = .horizontal
import UIKitclass 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 }}