UICollectionView中的动画滚动到项目并不总是有效

Nat*_*ohl 5 scroll objective-c animated ios uicollectionview

问题

我想让UICollectionView对特定项目进行动画滚动.

这大部分时间都有效,但偶尔我尝试滚动的项目最终不会被显示.

- (void)onClick {
  // (Possibly recompute the _items array.)
  NSInteger target_idx = // (...some valid index of _items)
  NSIndexPath *item_idx = [NSIndexPath indexPathForItem:target_idx inSection:0];
  [self scrollToItem:item_idx];
}

- (void)scrollToItem:(NSIndexPath*)item_idx {
  // Make sure our view is up-to-date with the data we want to show.
  NSInteger num_items = [self.collection_view numberOfItemsInSection:0];
  if (num_items != _items.count) {
    [self.collection_view reloadData];
  }

  [self.collection_view 
    scrollToItemAtIndexPath:item_idx
           atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                   animated:YES];
}
Run Code Online (Sandbox Code Playgroud)

细节

  • self.collection_view 是一个UICollectionView,由一行项目组成,启用标准流程布局和水平滚动.
  • 我需要reloadData在滚动之前调用,因为_items自UICollectionView上次显示它以来可能已经改变了.
  • 问题只发生在动画滚动上; 如果我通过,animated:NO那么一切都按预期工作.
  • 当问题发生时,后滚动调用indexPathsForVisibleItems显示UICollectionView 不认为目标项是可见的.

有时为什么无声地滚动到项目的任何想法都会失败?


更新:问题似乎来自重新加载和快速连续滚动; 如果没有重新加载,则滚动操作符合预期.加载新数据后是否有滚动到项目的习语?

Ben*_*ohn 11

在向a添加项目后,我刚刚遇到类似/相同的问题UICollectionView.

发生了什么

这个问题似乎是,紧随其后的[collectionView reloadData][collectionView insertItemsAtIndexPaths: @[newItemIndexPath]],集合视图的内容大小尚未更新.

如果您然后尝试滚动添加的项目可见,它将失败,因为内容rect尚未包含新项目的空间.

修复

有一个简单而相当强大的工作,即将滚动事件发布到运行循环的下一个迭代,如下所示:

const NSUInteger newIndex = 
  [self collectionView: self.collectionView numberOfItemsInSection: 0] - 1;

NSIndexPath *const newPath = 
  [NSIndexPath indexPathForItem: newIndex
                      inSection: 0];

[self.collectionView insertItemsAtIndexPaths: @[newPath]];

UICollectionViewLayoutAttributes *const layoutAttributes =
  [self.collectionView layoutAttributesForItemAtIndexPath: newPath];

dispatch_async(dispatch_get_main_queue(), ^{
    [self.collectionView scrollRectToVisible: layoutAttributes.frame 
                                    animated: YES];
});
Run Code Online (Sandbox Code Playgroud)

是不是有更好的解决方案?

虽然它的工作原理,这个"在下一个运行循环时发布滚动调用滴答"的恶作剧让人觉得很烦人.

最好UICollectionView是在完成更新内容rect时调用回调.对于对其模型执行异步更新的其他方法,UIKit对此样式进行了回调.例如,UIViewController转换和UIView动画的完成块.

UICollectionView不提供此回调.据我所知,在完成更新时没有其他简单的干净方法可供查找.如果没有这个,下一个运行循环tick是我们希望使用的回调独角兽的可行马代理.

还有什么需要知道的吗?

知道UITableView这个问题也很有用.类似的解决方法也应该在那里工作.