考虑一个标准的垂直滚动流布局,其中填充了足够的单元格以进行滚动。当滚动到底部时,如果删除项目,使得集合视图的内容大小必须缩小以容纳新项目数(即删除底行上的最后一项),则从中滚动的单元格行顶部是隐藏的。在删除动画结束时,顶行显示没有动画 - 这是一个非常不愉快的效果。
慢动作:
重现真的很简单:
创建新的单一视图项目,并将默认ViewController
更改为UICollectionViewController
的
将UICollectionViewController
添加到使用标准流布局的故事板,并将其类更改为ViewController
。为单元格原型提供标识符“Cell”,大小为200x200。
将以下代码添加到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
在处理集合视图时,我已经看到了这个问题的其他表现形式,只是上面的例子似乎最简单地证明了这个问题。 UICollectionView
似乎在默认动画期间陷入某种瘫痪的恐慌状态,并且在动画完成之前拒绝取消隐藏某些细胞。它甚至可以防止隐藏单元格上cell.hidden = NO
的人工调用产生影响(之后hidden
仍为YES
)。如果您可以获取对要取消隐藏的单元格的引用,则可以下拉到底层并设置hidden
,这在处理尚未显示的单元格时非常重要。
-initialLayoutAttributesForAppearingItemAtIndexPath
时,每个项目都会调用 deleteItemsAtIndexPaths:
,而不是滚动到视图中的项目。之后可以通过立即调用批量更新块中的reloadData
来解决问题,这似乎使集合视图意识到顶行即将出现:
[collectionView deleteItemsAtIndexPaths:@[indexPath]];
[collectionView performBatchUpdates:^{
[collectionView reloadData];
} completion:nil];
但不幸的是,这对我来说不是一个选择。我试图通过操纵单元格层来实现一些自定义动画计时。动画,并且调用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
)
因此,尽管从布局对象接收到所有正确的响应,但是集合视图似乎设置了某种标志,以便在更新期间不更新单元格。在UICollectionView
内有一个私有方法从layoutSubviews
内调用,似乎负责刷新单元格的外观:_updateVisibleCellsNow:
。这是在应用单元格起始属性之前最终要求数据源获取新单元格的地方,并且看起来这是失败的点,因为它应该被调用。
此外,这似乎与更新动画有关,或者至少单元格未针对插入/删除的持续时间进行更新。例如,以下工作没有毛刺:
- (void)addCell
{
NSIndexPath *indexPathToInsert = [NSIndexPath indexPathForItem:self.numberOfItems
inSection:0];
self.numberOfItems++;
[self.collectionView insertItemsAtIndexPaths:@[indexPathToInsert]];
[self.collectionView scrollToItemAtIndexPath:indexPathToInsert
atScrollPosition:UICollectionViewScrollPositionCenteredVertically
animated:YES];
}
如果在插入的单元格位于当前可见边界之外的情况下调用上述方法插入单元格,则插入项目时不会生成动画,集合视图会滚动到该单元格,在途中正确地出列并显示单元格。
iOS 7中出现问题& iOS 8 beta 5.
答案 0 :(得分:0)
调整内容插入内容,使其超出设备屏幕大小的范围。
collectionView.contentInsets = UIEdgeInsetsMake(-5,0,0,0); //Adjust this value until it looks ok