skip to Main Content

How can I implement such layout using UICollectionView in UIKit? I need to add a new cell which height is greater than height of normal cell. AFAIK all cells in one row of UICollectionView must have the same height.

portrait mode

landscape mode

2

Answers


  1. you can set that specific cell size in the below way:

    extension YourViewController: UICollectionViewDelegateFlowLayout {
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            if indexPath.item == /*your desired cell index*/ {
                return CGSize(width: normalWidth, height: doubleSizeHeight)            
            } else {
                return CGSize(width: normalWidth, height: normalHeight)
            }
        }
    }
    
    Login or Signup to reply.
  2. I think UICollectionViewCompositionalLayout will fit this scenario. It supports complex collection UI, like waterfall, nested cells, etc. And based on your requirements, it could be:

    //Defining sections
    enum Section: Int, Hashable {
        case waterfall
        case normal
    }
    
    //And layouts for sections
    let layout = UICollectionViewCompositionalLayout {
        (sectionIndex: Int, layoutEnvironment: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
        guard let sectionKind = Section(rawValue: sectionIndex) else { return nil}
    
        let section: NSCollectionLayoutSection
    
        if sectionKind == .waterfall {
            let leadingItem = NSCollectionLayoutItem(
                layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.7),
                                                   heightDimension: .fractionalHeight(1.0)))
            leadingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
    
            let trailingItem = NSCollectionLayoutItem(
                layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                   heightDimension: .fractionalHeight(0.3)))
            trailingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
            let trailingGroup = NSCollectionLayoutGroup.vertical(
                layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.3),
                                                   heightDimension: .fractionalHeight(1.0)),
                subitem: trailingItem, count: 2)
    
            let nestedGroup = NSCollectionLayoutGroup.horizontal(
                layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                   heightDimension: .fractionalHeight(0.4)),
                subitems: [leadingItem, trailingGroup])
            section = NSCollectionLayoutSection(group: nestedGroup)
    
        } else {
            let leadingItem = NSCollectionLayoutItem(
                layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
                                                   heightDimension: .fractionalHeight(1.0)))
            leadingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
    
            let trailingItem = NSCollectionLayoutItem(
                layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
                                                   heightDimension: .fractionalHeight(1.0)))
            trailingItem.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
    
            let nestedGroup = NSCollectionLayoutGroup.horizontal(
                layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                                   heightDimension: .fractionalHeight(0.2)),
                subitems: [leadingItem, trailingItem])
            section = NSCollectionLayoutSection(group: nestedGroup)
        }
    
        return section
    }
    
    collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
    

    The following is an explanation of the layouts above (all with insets):

    waterfall: A section that has (width: 1.0, height: 0.4) ratio with collectionView frame. It contains a leading cell item with (width: 0.7, height: 1.0). And a pair of trailing vertical cells with (width: 0.3, height: 1.0). Each cell in this group will have (width: 1.0, height: 0.3) to their group

    normal: A section that has (width: 1.0, height 0.2) ratio with collectionView frame. The layout will present two cells and each cell will get 0.5 width of collectionView.

    UPDATED: I used UICollectionViewDiffableDataSource, configured it is simple:

    //A struct to represent cell data. Notice: it has to conform Hashable
    struct CellData: Hashable {
        var title: String = ""
        var identifier: UUID = .init()
    
        init(_ title: String) {
            self.title = title
        }
    }
    
    //A dataSource represents collectionView data
    var dataSource: UICollectionViewDiffableDataSource<Section, CellData>
    

    Then append sections and items like you want:

    //Simple text cell with a label
    let cellRegistration = UICollectionView.CellRegistration<TextCell, CellData> { (cell, indexPath, identifier) in
        cell.label.text = identifier.title
        //Other configurations
    }
    
    dataSource = UICollectionViewDiffableDataSource<Section, CellData>(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in
        collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: item)
    })
    
    var snapshot = NSDiffableDataSourceSnapshot<Section, CellData>()
    snapshot.appendSections([.waterfall])
    let dummyWaterfallSection: [CellData] = [
        .init("1"),
        .init("2"),
        .init("3"),
    ]
    snapshot.appendItems(dummyWaterfallSection, toSection: .waterfall)
    
    snapshot.appendSections([.normal])
    let dummyNormalSection: [CellData] = [
        .init("11"),
        .init("22"),
        .init("33"),
        .init("44"),
        .init("55"),
    ]
    snapshot.appendItems(dummyNormalSection, toSection: .normal)
    
    dataSource.apply(snapshot, animatingDifferences: false)
    

    Output:

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search