Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UICollectionView compositional layout supplementary crash

I want to add separators on list-like collection view built with compositional layout. I know there is a solution in iOS 14, but I support iOS 13. I try to add separators as supplementary view to every cell in section.

First, I register my supplementary in collection view.

collectionView.register(
    supplementaryClass: CollectionReusableView<SeparatorView>.self,
    ofKind: .separator
)

Then I provide actual view in DataSource.

func collectionView(
    _ collectionView: UICollectionView,
    viewForSupplementaryElementOfKind kind: String,
    at indexPath: IndexPath
) -> UICollectionReusableView {
    switch kind {
    case UICollectionView.SupplementaryKind.separator.rawValue:
        return collectionView.dequeue(
            decorationClass: CollectionReusableView<SeparatorView>.self,
            ofKind: .separator,
            for: indexPath
        )
    default:
        assertionFailure("Unexpected element kind: \(kind).")
        return UICollectionReusableView()
    }
}

And finally, I layout item with separator.

let separator = NSCollectionLayoutSupplementaryItem(
    layoutSize: separatorSize,
    elementKind: UICollectionView.SupplementaryKind.separator.rawValue,
    containerAnchor: separatorAnchor
)
let item = NSCollectionLayoutItem(
    layoutSize: itemSize,
    supplementaryItems: [separator]
)

When I open my screen and UICollectionView.reloadData() is called, app crashes with this: Every supplementary must have a unique elementKind

What am I doing wrong? Xcode 13.3

like image 889
Serge Sayfulin Avatar asked Dec 02 '25 22:12

Serge Sayfulin


1 Answers

Because each NSCollectionLayoutSupplementaryItem should have a unique element kind.

Each type of supplementary item must have a unique element kind. Consider tracking these strings together in a way that makes it straightforward to identify each element

I've solved this issue by creating new SupplementaryViewOfKind on UICollectionViewCompositionalLayoutSectionProvider.

let uniqueItemId: String = /* */

let elementKind: String = UICollectionView.SupplementaryKind.separator.rawValue + uniqueItemId

/* register a new elementKind on UICollectionView */
collectionView.register(ReusableView.self, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: reuseIdentifier)

let separator = NSCollectionLayoutSupplementaryItem(
    layoutSize: separatorSize,
    elementKind: elementKind,
    containerAnchor: separatorAnchor
)
let item = NSCollectionLayoutItem(
    layoutSize: itemSize,
    supplementaryItems: [separator]
)

on your collectionView(_:viewForSupplementaryElementOfKind:at:), it should be

func collectionView(
    _ collectionView: UICollectionView,
    viewForSupplementaryElementOfKind kind: String,
    at indexPath: IndexPath
) -> UICollectionReusableView {
    if kind.contains(UICollectionView.SupplementaryKind.separator.rawValue) {
        return collectionView.dequeue(
            decorationClass: CollectionReusableView<SeparatorView>.self,
            ofKind: .separator,
            for: indexPath
        )
    } else {
        assertionFailure("Unexpected element kind: \(kind).")
        return UICollectionReusableView()
    }
}

But I don't think it's a great idea.


If you want to check that SupplementaryViewOfKind is already registered on UICollectionView, use private method named -[UICollectionView _hasRegisteredClassOrNibForSupplementaryViewOfKind:].

Then you can avoid repetition of same SupplementaryViewOfKind registration on UICollectionView, but it's not a necessary thing.

let uniqueItemId: String = /* */

let elementKind: String = UICollectionView.SupplementaryKind.separator.rawValue + uniqueItemId

let didRegister: Bool = collectionView._hasRegisteredClassOrNibForSupplementaryView(ofKind: elementKind)

if !didRegister {
    /* register a new elementKind on UICollectionView */
    collectionView.register(ReusableView.self, forSupplementaryViewOfKind: elementKind, withReuseIdentifier: reuseIdentifier)
}

let separator = NSCollectionLayoutSupplementaryItem(
    layoutSize: separatorSize,
    elementKind: elementKind,
    containerAnchor: separatorAnchor
)
let item = NSCollectionLayoutItem(
    layoutSize: itemSize,
    supplementaryItems: [separator]
)
like image 82
Jinwoo Kim Avatar answered Dec 04 '25 13:12

Jinwoo Kim



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!