在UICollectionView布局中自定义头的位置会导致NSInternalInconsistencyException错误

我试图自定义UICollectionView使用子类UICollectionViewFlowLayout类(基于宽松的代码堆叠标题显示input链接描述在这里 )的标头的位置。

作为一个最小的testing,让我们说我只是想添加一个固定的偏移量到所有标题的位置:

  • 我将所有的头添加到由layoutAttributesForElementsInRect返回的数组中,以便所有的头都被处理(这可能是问题的原因,我不确定)
  • 然后,我通过在layoutAttributesForSupplementaryViewOfKind添加一个固定的偏移来更新每个头

完整的实现包括在这篇文章的末尾。

(顺便说一句,我知道添加所有标题,包括rect之外的所有标题,在第一步中并不是严格意义上的必要,但这是一个简单的例子,我想创build一个更复杂的定制,这将导致所有标题将被显示在绘制rect)。

但是,当我运行代码时,我得到以下NSInternalInconsistencyException

 2014-01-15 00:41:50.130 CollectionStackedHeaders[60777:70b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'layout attributes for supplementary item at index path (<NSIndexPath: 0x8a7db90> {length = 2, path = 0 - 0}) changed from <UICollectionViewLayoutAttributes: 0x8a7f8b0> index path: (<NSIndexPath: 0x8a7d9c0> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 0; 320 50); to <UICollectionViewLayoutAttributes: 0x8a7fb80> index path: (<NSIndexPath: 0x8a7db90> {length = 2, path = 0 - 0}); element kind: (UICollectionElementKindSectionHeader); frame = (0 50; 320 50); zIndex = 1024; without invalidating the layout' 

看来这是由属性的更新引起的,好像我注释掉了下面两行它工作正常:

 attributes.zIndex = 1024; attributes.frame = frame; 

什么是导致这个错误,我能做些什么来让我的简单的例子启动和运行?

下面是这个简单例子的完整的类实现:

 @implementation myStackedHeaderFlowLayout - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect { // Call super to get elements NSMutableArray* answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; // As a test, always add first header to the answer array NSArray* indexes = [NSArray arrayWithObjects: [NSNumber numberWithInt:0], nil]; for (NSNumber* sectionNumber in indexes) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:[sectionNumber integerValue]]; UICollectionViewLayoutAttributes* layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; if (layoutAttributes) { [answer removeObject:layoutAttributes]; // remove if already present [answer addObject:layoutAttributes]; } } return answer; } - (UICollectionViewLayoutAttributes*)layoutAttributesForSupplementaryViewOfKind:(NSString*)kind atIndexPath:(NSIndexPath*)indexPath { // Call super to get base attributes UICollectionViewLayoutAttributes* attributes = [super layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; if ([kind isEqualToString:UICollectionElementKindSectionHeader]) { CGRect frame = attributes.frame; frame.origin.y += 50; // Update attributes position here - causes the problem attributes.zIndex = 1024; attributes.frame = frame; } return attributes; } - (UICollectionViewLayoutAttributes*)initialLayoutAttributesForAppearingSupplementaryElementOfKind:(NSString*)kind atIndexPath:(NSIndexPath*)indexPath { UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; return attributes; } - (UICollectionViewLayoutAttributes*)finalLayoutAttributesForDisappearingSupplementaryElementOfKind:(NSString*)kind atIndexPath:(NSIndexPath*)indexPath { UICollectionViewLayoutAttributes* attributes = [self layoutAttributesForSupplementaryViewOfKind:kind atIndexPath:indexPath]; return attributes; } - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound { return YES; } @end 

 layout attributes for supplementary item at index path (<NSIndexPath>) changed from <UICollectionViewLayoutAttributes> to <UICollectionViewLayoutAttributes> without invalidating the layout 

根据我的经验,当从layoutAttributesForElementsInRect:返回的数组包含两个具有相同索引path和(补充)元素类别的UICollectionViewLayoutAttributes对象时,抛出具有上述描述的NSInternalInconsistencyException

您正在收到此错误,因为您正在将frame(0 0; 320 50)(0 50; 320 50)而无需重新validation布局(可能是您无意中这样做)。

通常,这是因为您引用了两个不同布局元素的相同IndexPath ,但为每个布局元素提供了不同的frame值。

考虑以下:

 NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0]; UICollectionViewLayoutAttributes *newAttribute1 = [UICollectionViewLayoutAttributes layoutAttributesForDecorationViewOfKind:UICollectionElementKindSectionHeader withIndexPath:indexPath]; newAttribute1.frame = CGRectMake(0, 50, 320, 50); [attributes addObject:newAttribute1]; UICollectionViewLayoutAttributes *newAttribute2 = [UICollectionViewLayoutAttributes layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionFooter withIndexPath:indexPath]; newAttribute2.frame = CGRectMake(0, 0, 320, 50); [attributes addObject:newAttribute2]; 

每个使用相同的IndexPath ,因此它会导致NSInternalInconsistencyException

好吧,我不是100%肯定为什么,但用下面的代替layoutAttributesForElementsInRect似乎是诀窍:

 - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect { // Call super to get elements NSMutableArray* answer = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; NSUInteger maxSectionIndex = 0; for (NSUInteger idx=0; idx < [answer count]; ++idx) { UICollectionViewLayoutAttributes *layoutAttributes = answer[idx]; if (layoutAttributes.representedElementCategory == UICollectionElementCategoryCell || layoutAttributes.representedElementCategory == UICollectionElementCategorySupplementaryView) { // Keep track of the largest section index found in the rect (maxSectionIndex) NSUInteger sectionIndex = (NSUInteger)layoutAttributes.indexPath.section; if (sectionIndex > maxSectionIndex) { maxSectionIndex = sectionIndex; } } if ([layoutAttributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { // Remove layout of header done by our super, as we will do it right later [answer removeObjectAtIndex:idx]; idx--; } } // Re-add all section headers for sections >= maxSectionIndex for (NSUInteger idx=0; idx <= maxSectionIndex; ++idx) { NSIndexPath* indexPath = [NSIndexPath indexPathForItem:0 inSection:idx]; UICollectionViewLayoutAttributes *layoutAttributes = [self layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath]; if (layoutAttributes) { [answer addObject:layoutAttributes]; } } return answer; } 

我只能想象之前layoutAttributesForElementsInRect被称为早在我已经添加到第一部分的控制头被正确初始化,所以编程方式确定什么标题存在避免这个? 任何想法都会受到欢迎,但通过上述问题得到解决。

对我来说这个问题是由于粘滞标题布局,我解决了它使用PDKTStickySectionHeadersCollectionViewLayout