Sounds like you need everything a flow layout offers with a little customizations. Subclassing flow layout and overriding layoutAttributesForElements
should do the job:
A quick and dirty implementation will basically ask FlowLayout to do the layout for a given rect, then figure out what items appear in the last row. If there are less than 3 items, then space them out evenly.
class LastRowCenteredLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard var elementAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
guard elementAttributes.count > 0 else { return elementAttributes }
elementAttributes = elementAttributes.map { $0.copy() as! UICollectionViewLayoutAttributes }
let minY = elementAttributes.last!.frame.minY
let lastRowAttrs = elementAttributes.reversed().filter { $0.frame.minY == minY }
guard lastRowAttrs.count < 3,
let first = elementAttributes.first,
let last = elementAttributes.last else {
return elementAttributes
}
let horizontalPadding = rect.width - first.frame.minX - last.frame.maxX
let horizontalShift = horizontalPadding / 2.0
for attrs in lastRowAttrs {
attrs.frame = attrs.frame.offsetBy(dx: horizontalShift, dy: 0)
}
return elementAttributes
}
}
Note that if you plan on animating insertions/deletions, you should also override layoutAttributesForCellWithIndexPath:
and ensure it returns consistent results.
Apple has a guide on FlowLayout with a section dedicated to subclassing.
Sample Playground:
https://gist.github.com/AnuragMishra/0a694dfa9be1a5eab9fc7368b69812ad