强制滚动使其可见后,cellForItemAtIndexPath返回nil

17

我正在尝试编写一些代码,使集合视图滚动到特定的索引,然后获取对单元格的引用并执行一些逻辑。但是我注意到,如果该单元格在滚动之前不可见,cellForItemAtIndexPath调用将返回空值,导致我的其余逻辑失败。

[_myView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:index 
                                                     inSection:0] 
                atScrollPosition:UICollectionViewScrollPositionTop
                        animated:NO];

//Tried with and without this line, thinking maybe this would trigger a redraw
[_myView reloadData];

//returns nil if cell was off-screen before scroll
UICollectionViewCell *cell = 
  [_myView cellForItemAtIndexPath:
    [NSIndexPath indexPathForItem:index inSection:0]];

当滚动导致某个单元格突然出现时,是否需要调用其他方法以使cellForItemAtIndexPath返回该单元格的内容?


我无法重现你的问题。你能提供更多上下文吗?我的示例代码 - Timothy Moose
你在示例代码中使用了表视图,而我正在使用集合视图。 - Kevin DiTraglia
3
如果您需要引用您的单元格,那么我认为在您的设计中存在问题。您应该能够使用更改更新数据模型,滚动到单元格,然后您想要的内容就会出现。您不应该需要获取单元格并修改它。 - Aaron Brager
@AaronBrager是正确的。你的问题在于你的设计有问题,你试图访问视图而不是数据源。 - Tim
2
我只需要单元格的位置,因为在转换期间我正在将其他内容动画化到该位置。因此,我需要的不是数据源的一部分,而是它将在屏幕上绘制的位置的一部分。 - Kevin DiTraglia
显示剩余2条评论
4个回答

24

我现在想到了一个解决办法。如果在调用reloadData之后但在尝试获取单元格之前立即调用[myView layoutIfNeeded],那么一切都能正常工作。目前所有的单元格都被缓存,因此访问速度很快,但是如果我不得不从网络或内部数据库加载数据,我担心这可能会给我带来性能问题,不过我们会看看的。


1
我验证了这个方法对我也是有效的。我认为原因是AutoLayout传递并不会在每个可能的时候都执行。 - jaredsinclair

18

如果你想要访问单元格并在屏幕上可见:

NSIndexPath *indexPath = ...;

[collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically | UICollectionViewScrollPositionCenteredHorizontally animated:NO];

UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];

if(cell == nil) {
    [collectionView layoutIfNeeded];
    cell = [collectionView cellForItemAtIndexPath:indexPath];
}

if(cell == nil) {
    [collectionView reloadData];
    [collectionView layoutIfNeeded];
    cell = [collectionView cellForItemAtIndexPath:indexPath];
}

我很少进入第二个if语句,但是有两个备用选项非常有效。


谢谢,这真的很有帮助! - RPM
5
我寻找了很长时间,试图强制collectionView将某个单元格显示为焦点和选定状态,而您的方法(看起来很耗费)是唯一可行的解决方案。感谢您花时间提出建议。 - Shaybc

15

根据您的评论,听起来您最终想要的是单元格的框架。在不依赖单元格存在的情况下完成这一点的方法是询问集合视图的布局:

NSIndexPath *indexPath = ...;
UICollectionViewLayoutAttributes *pose = [self.collectionView.collectionViewLayout layoutAttributesForItemAtIndexPath:indexPath];
CGRect frame = pose.frame;

非常酷。现在唯一的问题是,我实际上要对单元格中子视图的位置进行动画处理,但使用这种方法无法获取该位置。因为我想象这种方式具有某些性能优势,所以我可以通过一些小技巧来解决它,因为它总是在同一个位置。 - Kevin DiTraglia
1
几个月后回顾,现在这就是解决方案,所以给你超级晚的选中标记。 - Kevin DiTraglia

4

hfossli的解决方案对我很有用,所以我提供了下面的Swift版本。在我的情况下,我无法获取对自定义UICollectionViewCell的引用,可能是因为它不可见。我尝试了很多方法,但正如Shaybc所说,这是唯一有效的解决方案。

Swift 3:

  func getCell(_ indexPath: IndexPath) -> CustCell? {
    cView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredHorizontally, animated: false)
    var cell = cView.cellForItem(at: indexPath) as? CustCell
    if cell == nil {
      cView.layoutIfNeeded()
      cell = cView.cellForItem(at: indexPath) as? CustCell
    }
    if cell == nil {
      cView.reloadData()
      cView.layoutIfNeeded()
      cell = cView.cellForItem(at: indexPath) as? CustCell
    }
    return cell
  }

用法:

let indexPath = IndexPath(item: index, section: 0)
cView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition(), animated: true)
if let cell = getCell(indexPath) {
      cell. <<do what you need here>>
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接