UICollectionView 使用自定义布局抛出未捕获的异常
Posted
技术标签:
【中文标题】UICollectionView 使用自定义布局抛出未捕获的异常【英文标题】:UICollectionView throws uncaught exception using custom layout 【发布时间】:2015-08-20 13:03:06 【问题描述】:我正在使用带有自定义布局的集合视图,我第一次从 API 收到的调用 web 服务返回 20 个对象,第二次它将返回 1 个对象,而重新加载数据应用程序会引发以下错误。
由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:UICollectionView 接收到具有索引的单元格的布局属性 不存在的路径:length = 2, path = 0 - 0
创建新布局的代码
-(void)doNewLayout
id<UICollectionViewDelegateJSPintLayout> delegate = (id<UICollectionViewDelegateJSPintLayout>)self.collectionView.delegate;
// get column width from delegate. If the method isn't implemented fall back to our property
NSUInteger columnWidth = self.columnWidth;
if(delegate && [delegate respondsToSelector:@selector(columnWidthForCollectionView:layout:)])
columnWidth = [delegate columnWidthForCollectionView:self.collectionView
layout:self];
// find out how many cells there are
NSUInteger cellCount = [self.collectionView numberOfItemsInSection:0];
// get max number of columns from the delegate. If the method isn't implemented, fall back to our property
NSUInteger maximumNumberOfColumns = self.numberOfColumns;
if(delegate && [delegate respondsToSelector:@selector(maximumNumberOfColumnsForCollectionView:layout:)])
maximumNumberOfColumns = [delegate maximumNumberOfColumnsForCollectionView:self.collectionView layout:self];
// build an array of all the cell heights.
NSMutableArray* cellHeights = [NSMutableArray arrayWithCapacity:cellCount];
for(NSUInteger cellIndex = 0; cellIndex < cellCount; ++cellIndex)
CGFloat itemHeight = self.itemHeight; // set default item size, then optionally override it
if(delegate && [delegate respondsToSelector:@selector(collectionView:layout:heightForItemAtIndexPath:)])
itemHeight = [delegate collectionView:self.collectionView
layout:self
heightForItemAtIndexPath:[NSIndexPath indexPathForItem:cellIndex
inSection:0]];
cellHeights[cellIndex] = @(itemHeight);
// now build the array of layout attributes
self.pendingLayoutAttributes = [NSMutableArray arrayWithCapacity:cellCount];
// will need an array of column heights
CGFloat* columnHeights = calloc(maximumNumberOfColumns,sizeof(CGFloat)); // calloc() initializes to zero.
CGFloat contentHeight = 0.0;
CGFloat contentWidth = 0.0;
for(NSUInteger cellIndex = 0; cellIndex < cellCount; ++cellIndex)
CGFloat itemHeight = [cellHeights[cellIndex] floatValue];
// find shortest column
NSUInteger useColumn = 0;
CGFloat shortestHeight = DBL_MAX;
for(NSUInteger col = 0; col < maximumNumberOfColumns; ++col)
if(columnHeights[col] < shortestHeight)
useColumn = col;
shortestHeight = columnHeights[col];
NSIndexPath* indexPath = [NSIndexPath indexPathForItem:cellIndex
inSection:0];
UICollectionViewLayoutAttributes* layoutAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
layoutAttributes.size = CGSizeMake(columnWidth,itemHeight);
layoutAttributes.center = CGPointMake((useColumn * (columnWidth + self.interitemSpacing)) + (columnWidth / 2.0),columnHeights[useColumn] + (itemHeight / 2.0));
self.pendingLayoutAttributes[cellIndex] = layoutAttributes;
columnHeights[useColumn] += itemHeight;
if(columnHeights[useColumn] > contentHeight)
contentHeight = columnHeights[useColumn];
CGFloat rightEdge = (useColumn * (columnWidth + self.interitemSpacing)) + columnWidth;
if(rightEdge > contentWidth)
contentWidth = rightEdge;
columnHeights[useColumn] += self.lineSpacing;
self.contentSize = CGSizeMake(contentWidth,contentHeight+100);
free(columnHeights);
任何快速解决方案将不胜感激。谢谢
【问题讨论】:
您可能需要显示更多代码,您如何告诉集合视图应该绘制多少个单元格? 添加您的自定义布局代码,似乎您没有为所有 inexPaths 创建 layoutAttributes,当您将项目插入到集合视图但不更改自定义布局时会发生这种情况 @C_X 添加了新布局创建的代码 sn-p。 所以这将计算一次或每一个新项目...你能在调用 collectionView.reloadData() 的地方添加代码吗? 哪一行导致了错误?添加异常断点。它还会显示当时的调用堆栈。 【参考方案1】:有几件事需要考虑,因为您是自定义布局,所以您需要为每个 indexPath 创建 layoutAttribute。在您的情况下,您的数据源数组计数和offerModel.arrayOffers
和self.pendingLayoutAttributes
应该是相同的,如果不是,那么如果offerModel.arrayOffers
拥有更多项目然后self.pendingLayoutAttributes
,则可能是问题及其可压碎的问题。
如果您正在异步加载数据,请确保在 arraysOffers 中添加行时还在 customLayout pendLayoutAttributes
中添加 layoutAttributes,我认为您目前没有这样做,通过添加方法并提供新的 @987654326 来做到这一点@ 到创建 layoutAttributes
的那个。
我经常这样
- (void)insertItems:(NSArray*)items
NSInteger startIndex = <start index for new item>;
[self.items addObjectsFromArray:items];
[self calculateLayoutForItems:items startIndex:startIndex];
这个方法会计算layoutAttributes
- (void)calculateLayoutForItems:(NSArray*)items startIndex:(NSInteger)startIndex
// collection view and loop over them as well and nest indexPath creation
NSInteger section = 0;
NSInteger endIndex = self.items.count;
// Lets create indexPaths for provided items and get there frames
for (NSInteger index = startIndex ;index < endIndex; index++)
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:section];
CGRect frame = [self frameForItemAtIndexPath:indexPath];// call your method for frame at indexPath
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
itemAttributes.frame = frame;
self.pendingLayoutAttributes[indexPath] = itemAttributes; // you can use your index
现在,当您在数据源中获得更多项目时,请在 customLayoutObject [self.customLayout insertItems:newItemsArray];
上调用此方法
此外,如果您已经存储了属性,那么它值得在自定义布局中覆盖 invalidate 方法以将所有属性重置为初始状态,然后
你可以在 reloadData
方法之前 invalidate
customLayout
,然后集合视图将强制它再次计算布局。
【讨论】:
感谢您的澄清。我添加了以下语句来重新加载数据,它似乎对我不起作用,并导致与我发布的相同问题。 [self.offerCollectionView.collectionViewLayout 无效布局]; [offerCollectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; [offerCollectionView reloadData]; 我已经用示例代码更新了我的答案,您可以如何在 customLyaout 中添加更多项目 您也可以使用'-performBatchUpdates:',这将使布局无效并自动重新加载数据。您应该在该块中插入/删除您的对象。 是的,可以添加项目然后更新,但问题是自定义布局中没有新 indexPaths 的布局属性可能是他正在使用将存储特定值的属性...尝试覆盖无效布局并重置一切以上是关于UICollectionView 使用自定义布局抛出未捕获的异常的主要内容,如果未能解决你的问题,请参考以下文章
具有自定义布局的 UICollectionView 变为空白
Swift 中的 UICollectionView 自定义布局
使用 Swift 3 在 UICollectionView 上自定义布局