Ben*_*nRB 7 ios uicollectionview uicollectionviewlayout
我有一个UICollectionView使用UICollectionViewFlowLayout的自定义子类,使部分标题贴在屏幕的顶部,同时滚动就像UITableView的简单样式.我的代码基于这种方法:
http://blog.radi.ws/post/32905838158/sticky-headers-for-uicollectionview-using
此外,集合视图设置为在用户滚动到底部时加载更多结果.也就是说,当用户到达集合视图的底部时,Web请求会加载更多数据,然后重新加载集合视图,即调用reloadData.
它似乎运行良好,除了我通过运行iOS 7和7.1的TestFlight从beta测试者那里得到一些崩溃报告,他们说当他们快速向下滚动到底部(即触发更多结果加载)时偶尔发生以下情况:
*** -[__NSArrayM objectAtIndex:]: index 28 beyond bounds [0 .. 16]
PRIMARY THREAD THREAD 0
__exceptionPreprocess
objc_exception_throw
-[__NSArrayM objectAtIndex:]
-[UICollectionViewFlowLayout(Internal) _frameForItemAtSection:andRow:usingData:]
-[UICollectionViewFlowLayout layoutAttributesForItemAtIndexPath:usingData:]
-[UICollectionViewFlowLayout layoutAttributesForItemAtIndexPath:]
-[MyCustomCollectionViewFlowLayout layoutAttributesForItemAtIndexPath:]
-[MyCustomCollectionViewFlowLayout layoutAttributesForSupplementaryViewOfKind:atIndexPath:]
-[MyCustomCollectionViewFlowLayout layoutAttributesForElementsInRect:]
-[UICollectionViewData validateLayoutInRect:]_block_invoke
-[UICollectionViewData validateLayoutInRect:]
-[UICollectionView layoutSubviews]
-[UIView(CALayerDelegate) layoutSublayersOfLayer:]
-[CALayer layoutSublayers]
Run Code Online (Sandbox Code Playgroud)
好像当我的自定义流布局代码调用[self.collectionView numberOfItemsInSection:someSection]以获取节的最后一个项的布局属性时,该调用基于新加载的数据返回(例如,在这种情况下,一个节现在有29个项)但是内部的默认流布局仍然使用某种缓存数据(例如,在这种情况下,该部分只有17个项目).不幸的是,我无法自己重现崩溃,即使是经历过它的beta测试人员也无法一致地重现它.
有什么想法吗?
小智 7
根据2 BenRB的注释,编辑2。
当dataSource得到更新并调用reloadData时,后者实际上会使集合视图中的所有内容失效。但是,初始化刷新过程的逻辑和确切顺序发生在默认流布局内部,对我们而言是隐藏的。
特别是,默认流布局具有其自己的私有方法_prepareLayout(恰好带有下划线),该方法独立于prepareLayout子类提供的方法及其重载。
prepareLayout顺便说一下,基本流布局类的默认实现(没有下划线)不执行任何操作。
在刷新过程中,默认流程布局为其子类提供了通过layoutAttributesForElementsInRect:和layoutAttributesForItemAtIndexPath:“回调” 提供更多信息(例如,额外的layoutAttributes)的机会 。为了保证基类的数据和相应的indexPath / layoutAttributes数组之间的一致性,对相应“ super”的调用应仅在以下相应方法内进行:
[super layoutAttributesForElementsInRect:] 仅在超载的内部 [layoutAttributesForElementsInRect:]
[super layoutAttributesForItemAtIndexPath:]仅在超载的内部[layoutAttributesForItemAtIndexPath:],
这些方法之间不应发生任何交叉调用,至少对于没有由其相应的“ super”方法提供的indexPath而言,因为我们不确切知道内部发生了什么。
我与集合视图进行了很长时间的斗争,最后以唯一的工作序列结束:
通过直接访问dataSource(无需中介视图的numberOfItemsInSection:的中介)来准备附加布局数据,并将该数据存储在子类的对象内部,例如,使用indexPath作为键存储在字典属性中。我正在超负荷运行[prepareLayout]。
当它通过回调请求此信息时,将存储的布局数据提供给基类:
// layoutAttributesForElementsInRect
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
//calls super to get the array of layoutAttributes initialised by the base class
**NSArray *array = [super layoutAttributesForElementsInRect:rect];**
for(MyLayoutAttributes *la in array)
if(la.representedElementCategory == UICollectionElementCategoryCell ){
NSIndexPath indexPath = la.indexPath //only this indexPath can be used during the call to layoutAttributesForItemAtIndexPath:!!!
//extracts custom layout data from a layouts dictionary
MyLayoutAttributes *cellLayout = layouts[la.indexPath];
//sets additional properties
la.this = cellLayout.this
la.that = cellLayout.that
...
}
....
return array;
}
Run Code Online (Sandbox Code Playgroud)
// layoutAttributesForItemAtIndexPath:
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
MyLayoutAttributes *la = (MyLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:indexPath ];
if(la.representedElementCategory == UICollectionElementCategoryCell ){
NSIndexPath indexPath = la.indexPath //only this indexPath can be used during the call !!!
//extracts custom layout data from a layouts dictionary using indexPath as a key
MyLayoutAttributes *cellLayout = layouts[la.indexPath];
//sets additional properties
la.this = cellLayout.this
la.that = cellLayout.that
}
return la;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10437 次 |
| 最近记录: |