Question

EDIT: for anyone having this problem, I've answered my question with a solution that worked for me, that you can find below.

Original Question

I'm trying to build a two-column, multi-section UICollectionView with two types of cells. There are two basic states (per section) I'm aiming for:

  • There is at least one item to show
    • show at least one cell representing the item, where each cell has half the width of the entire CollectionView
  • There is no item to show
    • show a special cell that represents the empty state which has a width of twice the normal cell (and thus it occupies the entire width of the CollectionView for that "row")

I've constructed what I believe to be the correct setup for this, including overriding the CollectionViewDelegateFlowLayout method below:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self trueSectionCount:indexPath.section] > 0 ? CGSizeMake(kItemCellWidth, kItemCellHeight) : CGSizeMake(kItemCellEmptyWidth, kItemCellEmptyHeight);
}

which returns an appropriate size matching some dimensions which are the same as the cells for both states.


My problem is as follows: After the first cell is created in the cellForItemAtIndexPath: method, and subsequently returned, an assertion failure is thrown, as follows:

*** Assertion failure in -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:applyAttributes:], /SourceCache/UIKit_Sim/UIKit-2935.137/UICollectionView.m:1289

After reading some / other / questions on similar topics, I have some theories.

I previously thought it had to do with the fact that I'm using two different sizes, but that can't be right since I'm manually overriding the required method as I showed above, and I'm never setting the itemSize property of my UICollectionViewFlowLayout object.

Another theory I had was that this may be caused by some funny business when registering my Nibs/Classes with the UICollectionView. Since I have two classes which could be used at any given time for item population, and from what I gathered from the comments in UICollectionView.h, specifically lines 102-104, there should only be one registered class/nib associated with the view. So I commented out the registration of the nibs and instead of calling dequeueReusableCellWithReuseIdentifier: in cellForItemAtIndexPath, I simply initialize a new Cell to nil and then allow my logic to create the new cell from its appropriate nib, as opposed to allowing the CollectionView to attempt and dequeue or spawn a new cell using it's own internal logic (which I know Apple put in place recently which differentiates a UICollectionView from a UITableView in this regard). Unfortunately, I'm still getting this assertion failure.

I've run out of ideas and I'd love to know if anyone can help me with this. Let me know if there is any more information I could have missed and I'll gladly provide it.

Was it helpful?

Solution

Solution to the Original Question

I've figured out what the problem was.

Basically I was correct in my theory that the problem was stemming from the registering of Nibs to be dequeued by the CollectionView. What I was doing was registering two different UICollectionView subclasses to be re-used, which is fine, except I was using the same Restoration Identifier for both, so the CollectionView didn't know which one to dequeue. I changed my code to register them as such:

[self.collectionView registerNib:[UINib nibWithNibName:@"SomeItemCell" bundle:nil] forCellWithReuseIdentifier:CellIdentifier];
[self.collectionView registerNib:[UINib nibWithNibName:@"SomeEmptyItemCell" bundle:nil] forCellWithReuseIdentifier:EmptyCellIdentifier];

and then subsequently dequeued the cells appropriately using their own Identifiers:

if (there are items to show in this section)
{
    SomeItemCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
    ... code ...
    return cell;
}
else
{
    SomeEmptyItemCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:EmptyCellIdentifier forIndexPath:indexPath];
    ... code ...
    return cell;
}

This ensured that the CollectionView knew exactly which cell to use. The sizing method I showed above is also correct, and if you're planning on using multiple cells that method is very important to get right to make everything work together nicely.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top