Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a grid layout with equal row heights using UICollectionViewCompositionalLayout

I've successfully been able to create a grid layout using UICollectionViewCompositionalLayout. However, I am unable to find a clever way of ensuring the UICollectionViewCell in each row in the grid are of the same height when their heights are dynamic. i.e. I want the cell to stretch to fill the available space height-wise in the row.

Desired layout:

  • Grid (3 x N)
  • Variable height cells
  • Each cell has the same height in a given row. (uses the max cell height in a row)
  • Small divider line at the bottom of each cell.
+------+--+------+--+------+
|~~~~~~|  |~~~~~~|  |~~~~~~|
|~~~~~~|  |~~~~~~|  |~~~~~~|
|~~~~~~|  |~~~~~~|  |      |
|~~~~~~|  |      |  |      |
--------  -------   --------   <--- divider
+------+--+------+--+------+

Note: The row itself is the correct height but I want the cell content to stretch to fill the available space because I need to insert a small "line-divider" at the bottom of each cell. I thought about using a supplementary view for this but it seemed sort of messy.

Current attempt:

func createLayout() -> UICollectionViewLayout {
    let estimatedHeight: CGFloat = 100

    let item = NSCollectionLayoutItem(
        layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(estimatedHeight)),
        supplementaryItems: []
    )

    let groupLayoutSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                 heightDimension: .estimated(estimatedHeight))
    let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupLayoutSize, subitem: item, count: 3)


    let section = NSCollectionLayoutSection(group: group)
    section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
    section.interGroupSpacing = 10
    let layout = UICollectionViewCompositionalLayout(section: section)
    return layout
}

Result:

+------+--+------+--+------+
|~~~~~~|  |~~~~~~|  |~~~~~~|
|~~~~~~|  |~~~~~~|  |~~~~~~|
|~~~~~~|  |~~~~~~|  --------
|~~~~~~|  --------  
--------    
+------+--+------+--+------+

Sample code: https://github.com/johnliedtke/grid-layout

like image 605
John Slade Avatar asked May 11 '20 16:05

John Slade


1 Answers

NSCollectionLayoutSupplementaryItem subclasses NSCollectionLayoutItem, so you should be able to add it as a subitem of your group. So you'd use

let item = NSCollectionLayoutItem(
        layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .fractional(1))
    )

let sup = NSCollectionLayoutSupplementaryItem(
            layoutSize: .init(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(2)),
            elementKind: "LineReusableView",
            containerAnchor: .init(edges: [.bottom])
        )
let threeItemGroup = NSCollectionLayoutGroup.horizontal(layoutSize: groupLayoutSize, subitem: item, count: 3)
let group = NSCollectionLayoutGroup.vertical(layoutSize: layoutGroupSize,subitems: [threeItemGroup,sup])

let section = NSCollectionLayoutSection(group: group)

This should result in you only having one line at the bottom of each group rather than your desired three lines, but you should be able to change how that looks in designing your supplementary view (making it draw 3 distinct lines rather than just 1).

like image 61
Hunter Meyer Avatar answered Nov 14 '22 23:11

Hunter Meyer