【问题标题】:UICollectionView BatchUpdate edge case failsUICollectionView BatchUpdate 边缘案例失败
【发布时间】: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 的插入它可以工作
  • 好收获!我对自己的假设完全错误(不确定它来自哪里,我想知道它是否曾经是真的?)我认为你是对的,从重新加载到删除和插入的转换完全有意义。有一个非常相似的崩溃,我无法通过删除和移动来重现,这加强了你的理论。用删除和插入替换重新加载似乎工作正常:) 你可以写一个答案来解释这一切,我会接受它。如果你不愿意,那很好,我会为后代做的。

标签: ios objective-c uicollectionview uikit performbatchupdates


【解决方案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];
}

【讨论】:

    猜你喜欢
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 2012-03-31
    • 1970-01-01
    • 1970-01-01
    • 2023-01-18
    • 2018-04-15
    相关资源
    最近更新 更多