UICollectionView BatchUpdate 边缘案例失败
UICollectionView BatchUpdate edge case fails
我在 UICollectionView 的 batchUpdate 操作中发现了一个简单的边缘情况,它应该可以工作但失败了
attempt to perform an insert and a move to the same index path ( {length = 2, path = 0 - 2})
我的操作是从[A, B] --> [C, B', A]。这是通过更新完成的:
- 从索引 0 移动到 2
- 重新加载索引 1
- 索引 0 处的插入
很明显错误不对,insert index和move TO index不一样
我设置了演示以确保这是一个 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 错误吗?
编辑:
另一个与此有关的崩溃:
attempt to perform a delete and a move from the same index path ( {length = 2, path = 0 - 5})
这似乎是一个错误,虽然 performBatchUpdates:completion
的文档解释了插入和删除操作中索引的上下文,并提到允许重新加载操作,但它没有详细说明重新加载操作中的索引。
经过一些实验,似乎 "under the covers" 重新加载是作为删除和插入实现的。这似乎会导致一些其他操作与重新加载索引之间存在重叠的问题。
不过,我确实发现用显式删除和插入替换重新加载似乎可行,因此您可以使用:
- (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 操作中发现了一个简单的边缘情况,它应该可以工作但失败了
attempt to perform an insert and a move to the same index path ( {length = 2, path = 0 - 2})
我的操作是从[A, B] --> [C, B', A]。这是通过更新完成的:
- 从索引 0 移动到 2
- 重新加载索引 1
- 索引 0 处的插入
很明显错误不对,insert index和move TO index不一样
我设置了演示以确保这是一个 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 错误吗?
编辑: 另一个与此有关的崩溃:
attempt to perform a delete and a move from the same index path ( {length = 2, path = 0 - 5})
这似乎是一个错误,虽然 performBatchUpdates:completion
的文档解释了插入和删除操作中索引的上下文,并提到允许重新加载操作,但它没有详细说明重新加载操作中的索引。
经过一些实验,似乎 "under the covers" 重新加载是作为删除和插入实现的。这似乎会导致一些其他操作与重新加载索引之间存在重叠的问题。
不过,我确实发现用显式删除和插入替换重新加载似乎可行,因此您可以使用:
- (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];
}