no UICollectionViewLayoutAttributes instance for -

2019-02-11 07:00发布

问题:

The title is the error I'm getting and I have no idea why, but here is some information so hopefully someone on here can elucidate me.

I have subclassed UICollectionViewFlowLayout as this saves me calculating the frames for the cell's myself (perhaps this is an issue?) in prepareLayout. I then use the UICollectionViewLayoutAttributes information to calculate a supplementary view that over lays it, I get the layout I desire.

I use performBatchUpdates:completion: to add,remove and update the view. Insertion works fine, however deleting items is when the error shown in the title appears.

So I know why the error is happening but i don't know why it should even be happening. To clarify with an example going through a scenario that causes the issue

  1. Start with 1 item with 1 supplementary view 1 section
  2. Add two more items (prepareLayout sees 3 items with 3 supplementary views)
  3. Delete item (prepareLayout sees 2 views with 2 supplementary views)
  4. layoutAttributesForSupplementaryViewOfKind:atIndexPath: is called asking for attributes for index path with section:0 and item:2
  5. Crash because it asked for attributes for a third supplementary view even though earlier it called prepare layout setting up 2 items and 2 supplementary views
  6. Throw hands up in resignation and despair

so the offending function as far as i can tell is:

- (UICollectionViewLayoutAttributes *)layoutAttributesForSupplementaryViewOfKind:(NSString *)elementKind atIndexPath:(NSIndexPath *)indexPath
{
    return self.layoutInfo[elementKind][indexPath];
}

which of course is called automagically by the internal networking of UICollectionView so I have no clue as to why it's asking for that supplementary view at that index path.

Anyone got any ideas? Perhaps it's how i use performBatchUpdates:completion: but deletion worked fine until adding supplementary views. I can provide more code/explanation as necessary.

回答1:

I scoured the forums looking for an answer and came across a few suggestions. None of them gave me the succour I needed, and eventually in the interest of meeting deadlines, I dropped the use of supplementary views altogether.

A few weeks later out of curiosity I looked around again and eventually came across the following post and now I'm back to using supplementary views again.

So, don't forget to return your:

- (NSArray<NSIndexPath *> *)indexPathsToDeleteForSupplementaryViewOfKind:(NSString *)elementKind
{
    return self.removedIndexPaths;
}

to your collection view layout.



回答2:

To prevent a crash you could return dummy attributes for all those indexPaths that are not valid any more. Something like this could help to prevent your crash:

UICollectionViewLayoutAttributes *layoutAttributes = self.layoutInfo[elementKind][indexPath]; // add some safety checks if this access creates an out of bounds issue

// create dummy layoutAttributes
// the workaround
if (layoutAttributes == nil) {
    UICollectionViewLayoutAttributes *dummyLayoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:elementKind withIndexPath:indexPath];
    dummyLayoutAttributes.frame = CGRectZero;
    dummyLayoutAttributes.hidden = YES;
    layoutAttributes = dummyLayoutAttributes;
}

return layoutAttributes;

This still leads to objects in your view stack that should not be there, but they are hidden and don't create a crash. The next time the UICollectionView updates it's layout it should clean out the old hidden views.