Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSCollectionViewFlowLayout - left alignment

NSCollectionViewFlowLayout produces a layout with items justified on the right margin or, if the container is only wide enough for one item, centres items. I was expecting an alignment option, e.g. on the delegate, but am not finding anything in the docs. Does it require subclassing NSCollectionViewFlowLayout to achieve this?

like image 832
Obliquely Avatar asked Nov 30 '22 16:11

Obliquely


2 Answers

Here is a subclass that produces a left justified flow layout:

class LeftFlowLayout: NSCollectionViewFlowLayout {

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [NSCollectionViewLayoutAttributes] {

        let defaultAttributes = super.layoutAttributesForElementsInRect(rect)

        if defaultAttributes.isEmpty {
            // we rely on 0th element being present,
            // bail if missing (when there's no work to do anyway)
            return defaultAttributes
        }

        var leftAlignedAttributes = [NSCollectionViewLayoutAttributes]()

        var xCursor = self.sectionInset.left // left margin

        // if/when there is a new row, we want to start at left margin
        // the default FlowLayout will sometimes centre items,
        // i.e. new rows do not always start at the left edge

        var lastYPosition = defaultAttributes[0].frame.origin.y

        for attributes in defaultAttributes {
            if attributes.frame.origin.y > lastYPosition {
                // we have changed line
                xCursor = self.sectionInset.left
                lastYPosition = attributes.frame.origin.y
            }

            attributes.frame.origin.x = xCursor
            // by using the minimumInterimitemSpacing we no we'll never go
            // beyond the right margin, so no further checks are required
            xCursor += attributes.frame.size.width + minimumInteritemSpacing

            leftAlignedAttributes.append(attributes)
        }
        return leftAlignedAttributes
    }
}
like image 186
Obliquely Avatar answered Dec 05 '22 01:12

Obliquely


@Obliquely's answer fails when the collectionViewItems are not uniform in height. Here is their code modified to handle non-uniformly-sized items in Swift 4.2:

class CollectionViewLeftFlowLayout: NSCollectionViewFlowLayout
{
    override func layoutAttributesForElements(in rect: CGRect) -> [NSCollectionViewLayoutAttributes]
    {
        let defaultAttributes = super.layoutAttributesForElements(in: rect)

        if defaultAttributes.isEmpty {
            return defaultAttributes
        }

        var leftAlignedAttributes = [NSCollectionViewLayoutAttributes]()

        var xCursor = self.sectionInset.left                            // left margin
        var lastYPosition = defaultAttributes[0].frame.origin.y         // if/when there is a new row, we want to start at left margin
        var lastItemHeight = defaultAttributes[0].frame.size.height

        for attributes in defaultAttributes
        {
            // copy() Needed to avoid warning from CollectionView that cached values are mismatched
            guard let newAttributes = attributes.copy() as? NSCollectionViewLayoutAttributes else {
                continue;
            }

            if newAttributes.frame.origin.y > (lastYPosition + lastItemHeight)
            {
                // We have started a new row
                xCursor = self.sectionInset.left
                lastYPosition = newAttributes.frame.origin.y
            }

            newAttributes.frame.origin.x = xCursor

            xCursor += newAttributes.frame.size.width + minimumInteritemSpacing
            lastItemHeight = newAttributes.frame.size.height

            leftAlignedAttributes.append(newAttributes)
        }
        return leftAlignedAttributes
    }
}
like image 24
Bryan Avatar answered Dec 05 '22 01:12

Bryan