I am trying to understand why Collection View keeps centre aligning just the last cell in a collection.
I have created a simple Flow layout based collection view. I am
In Swift 5
Use UICollectionViewFlowLayout to align cell to left like below
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
self.collectionView.collectionViewLayout = layout
When the last cell (only) is showing, try view debugging -> capture view hierarchy (ie. assuming you ran in simulator). My hunch: your cell is growing in size once you have only one left
If the only requirement difference to a normal UICollectionViewFlowLayout
is to ensure that the first item is always left-aligned, it should be enough to simply do just that:
class MyCustomFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
attributes?.first?.frame.origin.x = sectionInset.left
return attributes
}
}
The downside of this is that it will only work for the first item of the first section. Alternatively, we can use the index path which conveniently comes with the element attributes:
class MyCustomFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
attributes?.forEach { (elementAttributes) in
if elementAttributes.indexPath.item == 0 {
elementAttributes.frame.origin.x = sectionInset.left
}
}
return attributes
}
}
It is also possible to target only cells by (in the example above) checking elementAttributes.representedElementCategory
, documented here.
I managed to solve this using the below two steps:
UICollectionViewFlowLayoutAutomaticSize
CustomViewFlowLayout
( can't find the original SO/github question where I saw this approach used - I will add it if I find it) I then added the UICollectionViewFlowLayoutAutomaticSize
setting - and it's working beautifully. Sample code:
class MyLeftCustomFlowLayout:UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
var leftMargin = sectionInset.left
var maxY: CGFloat = 2.0
let horizontalSpacing:CGFloat = 5
attributes?.forEach { layoutAttribute in
if layoutAttribute.frame.origin.y >= maxY
|| layoutAttribute.frame.origin.x == sectionInset.left {
leftMargin = sectionInset.left
}
if layoutAttribute.frame.origin.x == sectionInset.left {
leftMargin = sectionInset.left
}
else {
layoutAttribute.frame.origin.x = leftMargin
}
leftMargin += layoutAttribute.frame.width + horizontalSpacing
maxY = max(layoutAttribute.frame.maxY, maxY)
}
return attributes
}
In the ViewController - you have to add the flow layout to the collection view to make it work:
let layout = MyLeftCustomFlowLayout()
layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
myCollectionViewReference.collectionViewLayout = layout
I think the implementation for Collection View can definitely be simplified but I am going to look into it more as I can see how powerful it is.