Say you're using a completely typical layout:
let layout = UICollectionViewCompositionalLayout { (_, _) -> NSCollectionLayoutSection? in
let layoutSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1/CGFloat(3)),
heightDimension: .absolute(rowHeight)
)
let item = NSCollectionLayoutItem(layoutSize: layoutSize)
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .absolute(rowHeight)
)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
return NSCollectionLayoutSection(group: group)
}
So that is a 3† column layout:
However, occasionally there's an item in the section (and there's only one section) that is full width (or perhaps any specific size):
if daData[indexPath.item].whatever = .special { ...
Solutions could be something like ...
(A) ...
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize {
if .. return .. the default from the compositional layout
else .. return some specific size
But I believe that's not possible / I don't know how to return the "super" of #sizeForItemAt (and I don't know if that would indeed be the value from the compositional layout).
(B) ...
let layoutSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1/ item == 666 ? 1 : 3 ),
but I don't know how to use the indexPath inside compositional layout (you can use the section but not the index path, as far as I know)
(C) ...
widthDimension: estimated: .fractionalWidth(1/3),
...
groupSize .. similar idea
But I don't know how to set the width as both estimated and fractional.
Perhaps combined with #sizeForItemAt, but I don't know how to combine a column number compositional layout with estimated sizes with #sizeForItemAt
Is there a way?
In short ideally:
- most items would have a width creating two columns, but,
- based on the datasource, the occasional item would have a one-column width.
footnotes
† In practice you have to deal with rotation (or other animating shapes). You can do this amazingly elegantly in compositional layout ...
// let columnCount = self.bounds.width < self.bounds.height ? CGFloat(2) : CGFloat(3)
// more specifically, often best to use the whole screen ... in case of for example a slide-up panel
var columnCount = CGFloat(2)
if let glass = self.findViewController()?.view.bounds {
if glass.width > glass.height {
columnCount = CGFloat(3)
}
}
let layoutSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1/columnCount), .. etc
remembering to
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collection.collectionViewLayout.invalidateLayout() // for rotation changes
}


Here's a reasonably straightforward approach that uses
UICollectionViewFlowLayout. This sample lets you show cells in a fixed number of columns but allows some items to be shown full width.The trick to showing cells in fixed columns with a flow layout is calculating the proper item size. The calculated width needs to take into account the collection view's width, the safe area insets, the section insets, and the iter-item spacing between cells in a row.
Comments in the code provide more details.
As you can see, there is one arguable "flaw" with this use of a flow layout - rows that have only 1 or 2 cells don't keep those cells aligned to the left. This is solved by subclassing
UICollectionViewFlowLayoutand using the subclass.See Left Align Cells in UICollectionView for lots of solutions. The following example is based on this answer:
Change:
to:
and now you get: