美文网首页iOS常见问题ios专题iOS开发
[UICollectionView _endItemAnimat

[UICollectionView _endItemAnimat

作者: 萌小菜 | 来源:发表于2016-03-14 17:01 被阅读3791次

今天coding的时候出现了如题那个bug,在网上找了找没有找到具体的解决方法,不过大神们也给出了了问题原因方向:

  • 问题一般出现在操作cell或者section的时候,例如调用下例方法:
- (void)insertItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)deleteItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)reloadItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
  • 导致问题的原因应该是数据源的数量与操作cell后的cell的数量不一致,或者是由于- (NSInteger)collectionView:(UICollectionView*)collectionView numberOfItemsInSection:(NSInteger)section这个方法中返回值写死了导致的。

分析(可以忽略直接看最后)

  • 通过我自己测试发现,我在调用reloadData后,在很短的时间内又调用了insertItemsAtIndexPaths方法才导致崩溃的发生,但是通过打印发现在调用reloadData后,系统并没有紧接着调用numberOfItemsInSection数据源方法,所以可以得知collectionView调用数据源方法是异步的。
  • 假设现在请求下来10个数据,然后调用reloadData,然后在很短的时间内调用
    [self.userInfos insertObject:info atIndex:0];
    [self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForRow:0 inSection:0]]];

由于数据源方法是异步的,在此之前还没有调用数据源方法numberOfItemsInSection,所以此时collectionView并不知道自身有多少个cell(或者说collectionView的cell还没有创建出来),也就无法进行cell的添加删除和刷新,所以会提示InvalidationContext

  • 另外,在reloadData方法说明中,苹果已经给出提示:
    You should not call this method in the middle of animation blocks where items are being inserted or deleted. Insertions and deletions automatically cause the table’s data to be updated appropriately.
    不可以在插入和删除cell的时候调用reloadData方法。

解决方法

延时调用insertItemsAtIndexPaths方法,也就是在collectionView调用完数据源方法后再进行cell的操作。一定要这样写(时间自己设定)

或者让collectionView主动调用一次据源方法,知道自己有多少个cell后才能操作cell,这样写:
(2016.9.21更新)

由于此问题出现的概率不大,所以对于测试问题解决方式来说有很多不确定性。之前的方法我本以为彻底解决了问题,没想到上线后同样的问题又出现了,很是无奈,只好另外再找解决方法,使用下面方法到现在基本上没有再出崩溃的问题,大家可以参考测试:

  • 删除时
[self.infos removeLastObject];
if ([self.collectionView numberOfItemsInSection:0] == self.infos.count) {
      [self.collectionView reloadData];
}else{
      [self.collectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:self.infos.count-1 inSection:0]]];
 }
  • 添加时
[self.infos insertObject:info atIndex:0];
    
if (self.infos.count == 1 || [self.collectionView numberOfItemsInSection:0] == self.infos.count) {
        [self.collectionView reloadData];
}else{
        [self.collectionView insertItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:0 inSection:0]]];
}

说明

  • 就是在调用insertItemsAtIndexPathsdeleteItemsAtIndexPaths之前要判断下当前cell数量是否和model数量相同,如果相同的话则不应该再插入或者删除cell
  • 插入cell时如果插入的是第一个(插入之前cell数量为0)时也不要调用insertItemsAtIndexPaths,而是调用reloadData

完成

相关文章

网友评论

  • Junq___:numberOfItemsInSection写固定值了吧,我就是这个错误
  • mysteryemm:个人小建议:在调用完`[self.collectionView reloadData]`之后,紧接着调用`[self.collectionView layoutIfNeeded]`,这样能保证内部同步回调代理方法,之后再执行数据源以及cell的增、删等操作,就不会因为操作前后number of items不相等而crash. 当然还有一种小技巧,就是在`[self.collectionView reloadData]`和数据源增删操作之间,显示的调用一次numberOfItemsInSection:
  • 897948f5dfd3:牛逼啊..
  • 倚楼听风雨wing:[self.infos removeLastObject];
    if ([self.collectionView numberOfItemsInSection:0] == self.infos.count) { // 条件总是成立的
    [self.collectionView reloadData];
    }else{ // 根本来不到这里
    [self.collectionView deleteItemsAtIndexPaths:@[[NSIndexPath indexPathForItem:self.infos.count-1 inSection:0]]];
    }

    如果在[self.collectionView numberOfItemsInSection:0]这个代理方法中返回self.infos.count的话
    [self.collectionView numberOfItemsInSection:0] == self.infos.count是总是相等的
    e2028fcc8811:[self.collectionView numberOfItemsInSection:0] 这个并不是代理哦,代理是要这样写的 [self.collectionView.dataSource numberOfItemsInSection:0]
  • dec7c847c96d:牛逼。
  • 春泥Fu:__weak typeof(self)weakSelf = self;
    [weakSelf.collectionView performBatchUpdates:^{
    [weakSelf.collectionView deleteItemsAtIndexPaths:@[indexPath]];
    } completion:^(BOOL finished) {
    [weakSelf.collectionView reloadData];
    }];
    来宝:@春泥Fu 我试过这样还是有问题
    春泥Fu:@萌小菜 额~,我现在是这么用的,暂时没发现问题
    萌小菜:@春泥Fu 这个方法可以吗?之前测试好像不能根本解决
  • Crowdasola:第一个解决方法中的延时调用写在什么地方?添加完数据源之后?
    萌小菜:@IT狂人Bill 你有手动刷新吗?
    Crowdasola:@萌小菜 嗯,但是我在实现Pinterest式的瀑布流时候,每当触底请求数据去夹在的时候,经常没办法立刻刷新出来,或者根本没有刷新
    萌小菜:@IT狂人Bill 文章已更新 可以试一下新的解决方法
  • 没梦想的咸鱼2:如果是插入 section呢
    好像没有 主动调用 计算section数量的方法?
    萌小菜:@有梦想的咸鱼 首先你看下有没有先同步数据 再插入section

本文标题:[UICollectionView _endItemAnimat

本文链接:https://www.haomeiwen.com/subject/vjpflttx.html