删除UICollectionView边缘的单元格 – 单元格不会在滚动后立即出现

考虑一个标准的垂直滚动stream布局,填充足够的单元格以引起滚动。 当滚动到底部时,如果您删除一个项目,以便收集视图的内容大小必须缩小以容纳新的项目数(即删除最后一行的最后一项),那么从顶部隐藏。 在删除animation结束时,顶部一行出现没有animation – 这是一个非常不愉快的效果。

在慢动作中:

单元格不显示

重现真的很简单:

  1. 创build一个新的单个视图项目,并将默认的ViewController更改为UICollectionViewController的子类

  2. UICollectionViewController添加到使用标准stream布局的故事板,并将其类更改为ViewController 。 给单元格原型标识符“单元格”和一个200×200的大小。

  3. 将下面的代码添加到ViewController.m

 @interface ViewController () @property(nonatomic, assign) NSInteger numberOfItems; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.numberOfItems = 19; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.numberOfItems; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { return [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath]; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { self.numberOfItems--; [collectionView deleteItemsAtIndexPaths:@[indexPath]]; } @end 


附加信息

在处理集合视图时,我已经看到了这个问题的其他performanceforms,只是上面的例子看起来是最简单的。 UICollectionView似乎在默认animation中进入某种瘫痪状态,并拒绝取消某些单元格,直到animation完成。 它甚至可以防止对隐藏单元格的cell.hidden = NO手动调用产生效果(之后hidden仍然YES )。 如果您可以获取要取消隐藏的单元格的引用,那么在处理尚未显示的单元格时这是非平凡的。

在调用deleteItemsAtIndexPaths:时为每个可见的项目调用deleteItemsAtIndexPaths: ,但不是滚动到视图中的项目。 可以通过在批处理更新块中立即调用reloadData解决这个问题,这似乎使得集合视图意识到顶行即将出现:

 [collectionView deleteItemsAtIndexPaths:@[indexPath]]; [collectionView performBatchUpdates:^{ [collectionView reloadData]; } completion:nil]; 

但不幸的是,这不是我的select。 我正在尝试通过操作单元图层和animation来实现一些自定义的animation定时,并且通过导致不必要的布局callback,调用reloadData真的会抛出事情。


更新:一点调查

我将日志语句添加到了很多布局方法中,并通过一些堆栈帧来查看发生了什么问题。 最重要的是,我检查layoutSubviews被调用时,当集合视图要求布局对象( layoutAttributesForElementsInRect:布局属性,并在单元格上调用applyLayoutAttributes:时。

我期望看到像这样的一系列的方法:

 // user taps cell (to delete it) -deleteItemsAtIndexPaths: -layoutAttributesForElementsInRect: -finalLayoutAttributes...: // Called for the item being deleted -finalLayoutAttributes...: // \__ Called for each index path visible -initialLayoutAttributes...: // / when deletion started -applyLayoutAttributes: // Called for the item being deleted, to apply final layout attributes // collection view begins scrolling up -layoutSubviews: // Called multiple times as the -layoutAttributesForElementsInRect: // collection view scrolls // ... for any new set of // ... attributes returned: -collectionView:cellForItemAtIndexPath: -applyLayoutAttributes: // Sets the standard attributes for the new cell // collection view finishes scrolling 

大部分情况正在发生。 当视图滚动时布局被正确触发,并且集合视图正确地查询要显示的单元的属性的布局。 然而, collectionView:cellForItemAtIndexPath:和相应的applyLayoutAttributes:方法直到删除之后才被调用,最后一次调用布局会导致为其隐藏的单元格分配布局属性(设置hidden = NO )。

因此,尽pipe从布局对象接收到所有正确的响应,但收集视图有一些标志设置为在更新期间不更新单元。 在layoutSubviews中调用的UICollectionView上有一个私有方法,它似乎负责刷新单元格的外观: _updateVisibleCellsNow: 这是从数据源最终在应用单元格起始属性之前被请求新的单元格的地方看起来这是失败点,因为它不应该被调用。

此外,这似乎与更新animation有关 ,或者至less在插入/删除期间单元格不会更新。 例如下面的工作没有毛刺:

 - (void)addCell { NSIndexPath *indexPathToInsert = [NSIndexPath indexPathForItem:self.numberOfItems inSection:0]; self.numberOfItems++; [self.collectionView insertItemsAtIndexPaths:@[indexPathToInsert]]; [self.collectionView scrollToItemAtIndexPath:indexPathToInsert atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES]; } 

如果在插入的单元格位于当前可见边界之外的情况下调用上述方法来插入单元格,则插入的项目不带animation,并且集合视图会滚动到该单元格,正确地出队和显示单元格。

在iOS 7和iOS 8 beta 5中出现问题。

调整您的内容插图,以便它们略微超出设备屏幕大小的范围。

 collectionView.contentInsets = UIEdgeInsetsMake(-5,0,0,0); //Adjust this value until it looks ok