Ios UICollectionView批处理更新边缘案例失败
我在UICollectionView的batchUpdate操作中发现了一个简单的边缘情况,该操作应该可以工作,但在 尝试执行插入并移动到同一索引路径({length=2,path=0-2}) 我的操作是从[A,B]-->[C,B',A]开始。这是通过以下更新完成的:Ios UICollectionView批处理更新边缘案例失败,ios,objective-c,uicollectionview,uikit,performbatchupdates,Ios,Objective C,Uicollectionview,Uikit,Performbatchupdates,我在UICollectionView的batchUpdate操作中发现了一个简单的边缘情况,该操作应该可以工作,但在 尝试执行插入并移动到同一索引路径({length=2,path=0-2}) 我的操作是从[A,B]-->[C,B',A]开始。这是通过以下更新完成的: 从索引0移动到2 重新加载索引1 在索引0处插入 显然错误是错误的,插入索引与移动到索引不同 我设置了演示,以确保这是一个UICollectionView问题,如果您希望看到它的实际运行,请参阅我的代码: @implement
- 从索引0移动到2
- 重新加载索引1
- 在索引0处插入
@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})执行删除和移动
虽然
performbatchudates:completion
的文档解释了insert和delete操作中索引的上下文,并提到允许重新加载操作,但它没有详细说明重新加载操作中索引的上下文,这似乎是一个bug
经过一些实验,似乎“隐藏”的重新加载被实现为删除和插入。这似乎会导致其他操作与重新加载索引之间存在重叠的问题
但是,我确实发现,用显式删除和插入替换重新加载似乎有效,因此您可以使用:
- (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];
}
如果在移动之前执行插入操作,会怎么样?从0移动到2对只包含2个项的集合无效。不改变任何内容,也不应该改变任何内容。在batchUpdate中执行操作的顺序并不重要,它们是按照apple定义的顺序执行的(先删除,然后插入,然后移动)。“从索引移动”对应于其处于原始状态的索引,“到索引”相对于最终状态。它看起来像一个bug。仅供参考,使用
[\u集合moveItemAtIndexPath:[自索引:1]到IndexPath:[自索引:1]]代码>实际上不起作用,因为第二个单元格没有重新加载。我使用了颜色而不是数字,因此您可以看到发生了什么:只有当我将重新加载移动到完成块时,它才会起作用。如果我放弃移动,离开插入,然后尝试重新加载索引2处的项目,我会遇到一个错误,即当只有2个项目时,我尝试删除项目2。重新加载似乎转换为删除和插入,这会导致索引在批处理过程中发生更改的问题。如果我将重载替换为索引1的离散删除,然后为索引1插入,那么效果会很好!我完全错误地认为我的移动到了自身(不确定这是从哪里来的,我想知道它是否曾经是真的?)我想你是对的,从重新加载到删除和插入的转换是完全有意义的。有一个非常类似的崩溃,我无法通过删除和移动复制,这加强了你的理论。用删除和插入替换重新加载似乎效果不错:)你可以写一个答案来解释这一切,我会接受的。如果你不愿意,那很好,我会为子孙后代做的。