Stu*_*art 6 ios uicollectionview uicollectionviewcell uicollectionviewlayout
考虑一个标准的垂直滚动流布局,其中填充了足够的单元格以进行滚动.当滚动到底部时,如果删除项目,使得集合视图的内容大小必须缩小以容纳新项目数(即删除底行上的最后一项),则从中滚动的单元格行顶部是隐藏的.在删除动画结束时,顶行显示没有动画 - 这是一个非常不愉快的效果.
慢动作:

重现真的很简单:
创建一个新的单个视图项目,并将默认值更改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
Run Code Online (Sandbox Code Playgroud)
在处理集合视图时,我已经看到了这个问题的其他表现形式,只是上面的例子似乎最简单地证明了这个问题.UICollectionView在默认动画期间似乎陷入某种瘫痪的恐慌状态,并且在动画完成之前拒绝取消隐藏某些单元格.它甚至可以防止cell.hidden = NO对隐藏单元的手动调用产生影响(hidden仍然是YES之后).hidden如果您可以获取对要取消隐藏的单元格的引用,那么下拉到底层并进行设置是有效的,这在处理尚未显示的单元格时非常重要.
-initialLayoutAttributesForAppearingItemAtIndexPath正在为调用时可见的每个项目调用deleteItemsAtIndexPaths:,但不会调用滚动到视图中的项目.通过reloadData之后立即调用批量更新块可以解决问题,这似乎使集合视图意识到顶行即将出现:
[collectionView deleteItemsAtIndexPaths:@[indexPath]];
[collectionView performBatchUpdates:^{
[collectionView reloadData];
} completion:nil];
Run Code Online (Sandbox Code Playgroud)
但不幸的是,这对我来说不是一个选择.我试图通过操纵单元格层和动画来实现一些自定义动画计时,并且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
Run Code Online (Sandbox Code Playgroud)
大多数情况正在发生; 在视图滚动时正确触发布局,并且集合视图正确地查询布局以查看要显示的单元格的属性.但是,直到删除之后才会调用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];
}
Run Code Online (Sandbox Code Playgroud)
如果在插入的单元格位于当前可见边界之外的情况下调用上述方法来插入单元格,则插入项目时不会生成动画,并且集合视图会滚动到该单元格,在路上正确地出列并显示单元格.
iOS 7和iOS 8 beta 5出现问题.
小智 0
调整内容插图,使其稍微超出设备屏幕尺寸的范围。
collectionView.contentInsets = UIEdgeInsetsMake(-5,0,0,0); //Adjust this value until it looks ok
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2108 次 |
| 最近记录: |