4

I'm trying to make collection view with compositional layout which have multi sections

but if there is empty items in sections how can I deal with it?

if item is empty I don't want to show that section

UICollectionViewCompositionalLayout { (section, env) -> NSCollectionLayoutSection? in
  // do I have to code in this area? 
}

enter image description here

PrepareFor
  • 2,448
  • 6
  • 22
  • 36
  • Do you have an empty data source array for the first section (e.g. ```[[],[image, image, image ...]]```)? Or are there elements inside it (which are not visible, maybe because the image is nil)? – finebel Jul 30 '21 at 05:40
  • My guess is that he doesnt have any data in the array, but the layout doesn't know that and creates the UI anyway. If you use the !myAlbums.isEmpty to link the section to data, then the images from "Section title" section will populate the layout of section "My albums". – christostsang Aug 11 '22 at 12:05

3 Answers3

2

If you are also using UICollectionViewDiffableDataSource, you could deal with empty sections when you are creating/updating your snapshots - append only sections with items in it.

In my project I do something like this:

func performQuery(animate: Bool) {
    var currentSnapshot = NSDiffableDataSourceSnapshot<Section, ViewCell>()
    
    if !calendars.isEmpty {
        currentSnapshot.appendSections([Section(name: "main")])
        currentSnapshot.appendItems(calendars, toSection: Section(name: "main"))
    }

    if !lifeCals.isEmpty {
        currentSnapshot.appendSections([Section(name: "life")])
        currentSnapshot.appendItems(lifeCals, toSection: Section(name: "life"))
    }
    
    dataSource.apply(currentSnapshot, animatingDifferences: animate)
}

This way in case user has 0 life calendars, there will be no "life" section.

Viktor Gavrilov
  • 830
  • 1
  • 8
  • 13
  • 1
    This is not correct because if you hide the first section, then the data from second section will populate the layout of the first section, breaking the UI. The correct approach (the one I try to figure out) is to link the layouts with the object type of the data. – christostsang Aug 11 '22 at 12:02
  • 1
    @christostsang the data from second section will populate the layout of the first section if you are specifying Layout based on just `sectionIndex`. You could link Layout to particular objects by identifiying current section: `let section = dataSource.snapshot().sectionIdentifiers[sectionIndex]`. Nevertheless OP question was about hiding empty section – Viktor Gavrilov Aug 12 '22 at 06:53
0

I had the same problem and I fixed it like this:

import UIKit

final class CollectionViewCompositionalLayout: UICollectionViewCompositionalLayout {
    override init(sectionProvider: @escaping UICollectionViewCompositionalLayoutSectionProvider) {
        weak var weakSelf: CollectionViewCompositionalLayout?

        super.init { section, environment in
            guard
                let strongSelf = weakSelf,
                let collectionView = strongSelf.collectionView,
                let dataSource = collectionView.dataSource,
                dataSource.collectionView(collectionView, numberOfItemsInSection: section) == 0
            else {
                return sectionProvider(section, environment)
            }
            return strongSelf.emptySectionLayout()
        }

        weakSelf = self
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension CollectionViewCompositionalLayout {
    private enum Constants {
        static let size: CGFloat = 0.1
    }

    private func emptySectionLayout() -> NSCollectionLayoutSection {
        let size = NSCollectionLayoutSize(
            widthDimension: .estimated(Constants.size),
            heightDimension: .estimated(Constants.size)
        )
        let item = NSCollectionLayoutItem(
            layoutSize: size
        )
        let group = NSCollectionLayoutGroup.vertical(
            layoutSize: size,
            subitems: [item]
        )
        let emptySectionLayout = NSCollectionLayoutSection(group: group)
        return emptySectionLayout
    }
}

It doesn't look good but works pretty well :)

0

I have faced the same problem recently. Since I have just 3 or 2 sections I added layout with 2 sections dedicated for the empty case, and switch between them when updating data source.

Andrey Isaev
  • 192
  • 12