Search code examples
iosobjective-cuicollectionviewuicollectionviewlayout

Left Align Cells in UICollectionView


I am using a UICollectionView in my project, where there are multiple cells of differing widths on a line. According to: https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html

it spreads the cells out across the line with equal padding. This happens as expected, except I want to left justify them, and hard code a padding width.

I figure I need to subclass UICollectionViewFlowLayout, however after reading some of the tutorials etc online I just don't seem to get how this works.


Solution

  • The other solutions in this thread do not work properly, when the line is composed by only 1 item or are over complicated.

    Based on the example given by Ryan, I changed the code to detect a new line by inspecting the Y position of the new element. Very simple and quick in performance.

    Swift:

    class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
        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
        }
    }
    

    If you want to have supplementary views keep their size, add the following at the top of the closure in the forEach call:

    guard layoutAttribute.representedElementCategory == .cell else {
        return
    }
    

    Objective-C:

    - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
        NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
    
        CGFloat leftMargin = self.sectionInset.left; //initalized to silence compiler, and actaully safer, but not planning to use.
        CGFloat maxY = -1.0f;
    
        //this loop assumes attributes are in IndexPath order
        for (UICollectionViewLayoutAttributes *attribute in attributes) {
            if (attribute.frame.origin.y >= maxY) {
                leftMargin = self.sectionInset.left;
            }
    
            attribute.frame = CGRectMake(leftMargin, attribute.frame.origin.y, attribute.frame.size.width, attribute.frame.size.height);
    
            leftMargin += attribute.frame.size.width + self.minimumInteritemSpacing;
            maxY = MAX(CGRectGetMaxY(attribute.frame), maxY);
        }
    
        return attributes;
    }