UICollectionView BatchUpdate 边缘案例失败
Posted
技术标签:
【中文标题】UICollectionView BatchUpdate 边缘案例失败【英文标题】:UICollectionView BatchUpdate edge case fails 【发布时间】:2017-02-09 22:10:14 【问题描述】:我在 UICollectionView 的 batchUpdate 操作中发现了一个简单的边缘情况,它应该可以工作但失败了
尝试执行插入和移动到相同的索引路径(length = 2, path = 0 - 2)
我的操作是从[A, B] --> [C, B', A]开始。这是通过更新完成的:
从索引 0 到 2 的移动 重新加载索引 1 索引 0 处的插入显然错误是错误的,插入索引与移动TO索引不同。
我设置了演示以确保这是一个 UICollectionView 问题,如果您希望看到它的实际效果,这是我的代码:
@implementation ViewController
UICollectionView *_collection;
NSArray *_values;
- (instancetype)init
self = [super init];
if (self)
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
_collection = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];
_collection.dataSource = self;
_values = @[@1, @2];
[_collection registerClass:[UICollectionViewCell class]
forCellWithReuseIdentifier:@"reuse"];
[self.view addSubview:_collection];
return self;
- (void)viewDidLoad
[super viewDidLoad];
_collection.frame = self.view.bounds;
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void)
[self triggerBatchUpdate];
);
- (void)triggerBatchUpdate
[_collection performBatchUpdates:^
_values = @[@4, @5, @1];
[_collection insertItemsAtIndexPaths:@[[self index:0]]];
[_collection moveItemAtIndexPath:[self index:0] toIndexPath:[self index:2]];
// Works with this line
// [_collection moveItemAtIndexPath:[self index:1] toIndexPath:[self index:1]];
// Fails with this line
[_collection reloadItemsAtIndexPaths:@[[self index:1]]];
completion:nil];
- (NSIndexPath *)index:(NSUInteger)ind
return [NSIndexPath indexPathForRow:ind inSection:0];
- (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section
return _values.count;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath
UICollectionViewCell *cell = [_collection dequeueReusableCellWithReuseIdentifier:@"reuse"
forIndexPath:indexPath];
cell.backgroundColor = [UIColor grayColor];
return cell;
@end
如果我使用代码可以工作的事实
[_collection moveItemAtIndexPath:[self index:1] toIndexPath:[self index:1]];
而不是
[_collection reloadItemsAtIndexPaths:@[[self index:1]]];
让我怀疑。这两个操作应该是等价的。
知道这里发生了什么吗?这是我认为的 UICollectionView 错误吗?
编辑: 事实证明与此有关的另一个崩溃:
尝试从同一索引路径(length = 2, path = 0 - 5)执行删除和移动
【问题讨论】:
如果在移动之前执行插入会怎样?对于仅包含 2 个项目的集合,从 0 到 2 的移动是无效的 不,这不会改变任何东西,也不应该。您在 batchUpdate 中执行操作的顺序无关紧要,它们按照苹果定义的顺序执行(首先删除,然后插入,然后移动)。 move FROM索引对应其原始状态的索引,TO索引是相对于最终状态的。 它看起来像一个错误。仅供参考,使用[_collection moveItemAtIndexPath:[self index:1] toIndexPath:[self index:1]];
实际上不起作用,因为第二个单元格没有重新加载。我使用颜色而不是数字,所以你可以看到发生了什么:gist.github.com/paulw11/025e6f3dbdfaf911a6dab95c6631cbe2 只有当我将重新加载移到完成块时它才有效
如果我摆脱了移动,离开了插入,然后尝试在索引 2 处重新加载项目,我得到一个错误,我试图在只有 2 个项目时删除项目 2。似乎重新加载已转换为删除和插入,这会导致索引在批处理期间发生更改的问题。如果我用索引 1 的离散删除替换重新加载,然后索引 1 的插入它可以工作
好收获!我对自己的假设完全错误(不确定它来自哪里,我想知道它是否曾经是真的?)我认为你是对的,从重新加载到删除和插入的转换完全有意义。有一个非常相似的崩溃,我无法通过删除和移动来重现,这加强了你的理论。用删除和插入替换重新加载似乎工作正常:) 你可以写一个答案来解释这一切,我会接受它。如果你不愿意,那很好,我会为后代做的。
【参考方案1】:
这似乎是一个错误,尽管performBatchUpdates:completion
的文档解释了插入和删除操作中索引的上下文,并提到允许重新加载操作,但它没有详细说明重新加载操作中索引的上下文.
经过一些实验,似乎“在幕后”重新加载实现为删除和插入。这似乎会导致一些其他操作与重新加载索引之间存在重叠的问题。
但是,我确实发现用显式删除和插入替换重新加载似乎可行,因此您可以使用:
- (void)triggerBatchUpdate
[_collection performBatchUpdates:^
_values = @[[UIColor blueColor], [UIColor yellowColor], [UIColor redColor]];
[_collection moveItemAtIndexPath:[self index:0] toIndexPath:[self index:2]];
[_collection insertItemsAtIndexPaths:@[[self index:0]]];
[_collection deleteItemsAtIndexPaths:@[[self index:1]]];
[_collection insertItemsAtIndexPaths:@[[self index:1]]];
completion:nil];
【讨论】:
以上是关于UICollectionView BatchUpdate 边缘案例失败的主要内容,如果未能解决你的问题,请参考以下文章
如何滚动另一个 UICollectionView 下方的 UICollectionView?
UICollectionView 单元内部已加载,但 UICollectionView 未显示
UICollectionView 断言失败 -[UICollectionView _updateWithItems:tentativelyForReordering:]
UITableviewCell 中的 UICollectionView。添加约束后,UICollectionView 不显示