UIcollectionView cellForItemAtIndexPath在iOS 10中返回Null,而在iOS 9和iOS 8中正常工作。

3

我有一个应用程序已经成功运行了几年。 它在一个UICollectionView中检索RSS Feed。 cellForItemAtIndexPath方法设置文本并调用数据源方法从链接中指定的Feed加载图像。 如果不存在,则加载网页数据并搜索<img>标签以获取图像。 找到/加载图像后,委托方法将被调用以将图像添加到单元格中。 (如下)

在iOS 8和9上运行时一切都正常,但是在iOS 10上运行时,在加载RSS feed时可见单元格会更新图片,但是在滚动时不会添加任何图片,并且我会从cellForItemAtIndexPath获得NULL。

当我向后滚动时显示图像,如果我在imageWasLoadedForStory中添加reloadItemsAtIndexPaths,则显示图像,但reloadItemsAtIndexPaths会影响性能。

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
           // ...    

          if (story.thumbnail) {
          [imageView setAlpha: 1.0];
          imageView.image = story.thumbnail;
          UIActivityIndicatorView *lActivity = (UIActivityIndicatorView *) [collectionView viewWithTag: 900];
          [lActivity removeFromSuperview];
          }else{
               [story setDelegate: self];
               [story findArticleImageForIndexPath: indexPath];
          }
          //  ...
}



//delegate method.  Called when image has been loaded for cell at specified indexpath

- (void) imageWasLoadedForStory: (RSSStory *) story forIndexPath: (NSIndexPath *) indexPath
{
        //get cell
        CustomCollectionViewCell *customCell = (id) [self.collectionview cellForItemAtIndexPath: indexPath];

        NSLog(@"imageWasLoadedForStory row %i section %i  and class %@", (int)indexPath.row, (int)indexPath.section, [customCell class]);

        //if cell is visible ie: cell is not nil then update imageview
        if (customCell) {
                 UIImageView *imageView = (UIImageView *) [customCell viewWithTag: 300];
                 imageView.image = story.thumbnail;
                 UIActivityIndicatorView *lActivity = (UIActivityIndicatorView *) [customCell viewWithTag: 900];
                 [lActivity removeFromSuperview];
                 [customCell setNeedsLayout];
                 [customCell setNeedsDisplay];
                 }
                    //[self.collectionview reloadItemsAtIndexPaths: [NSArray arrayWithObject: indexPath]];           
}



- (void) findArticleImageForIndexPath: (NSIndexPath *) indexPath
{
           //kick off image search
           dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

                self.thumbnail = [self findArticleForStoryForIndexPath:indexPath];
                dispatch_async( dispatch_get_main_queue(), ^{
                //image set - return
                      [self.delegate imageWasLoadedForStory: self forIndexPath: indexPath];
                });
        });
}

好吧,我可能不是疯了。我已经能够通过使用标签属性使其工作。在cellForItemAtIndexPath中,我添加了:[cell setTag: indexPath.row];并在我的委托方法中添加了:CustomCollectionViewCell *customCell =(CustomCollectionViewCell *)[self.collectionview viewWithTag:indexPath.row];这使一切都正常工作,但我认为这不是处理此问题的规范方式。 - Joe Fratianni
3个回答

22

我鼓励每个人阅读有关在iOS 10中新增的Prefetching的内容。简单的解决方案如下:

[self.customCollectionview setPrefetchingEnabled:NO];

事实证明,现在的单元格是预取的。这意味着已加载/不可见的单元格和已加载/可见的单元格之间现在有了差异。

在iOS 10中,单元格现在可以预加载,但如果它不可见,则仍将返回nil。 因此,会调用cellForItemAtIndexPath来预加载单元格。 然后完全有可能图像完成加载并且cellForItemAtIndexPath将返回nil,如果该单元格不可见。 这意味着imageView将不会被设置。 滚动时,由于单元格已经创建,因此图像不会添加到单元格中。

在UITableView或UICollectionView上获取已加载与可见单元格


非常感谢你,乔 :)。 - Nada Gamal
1
很遗憾,你的解决方案在我的情况下不起作用。我在viewDidLoad()中设置了self.collectionView.isPrefetchingEnabled = false,但是当我在didDeselectItemAt indexPath:中调用cellForItem(at:)时,仍然返回nil - Marcel T
你所描述的情况肯定还有其他问题。(例如,错误的indexPath等) 如果有人选择了一个单元格,那么它必须被加载。预取仅适用于显示单元格之前。例如,用户向下滚动,单元格2到8可见。在iOS 10中,单元格9到15会被预加载,以便在需要时能够立即显示。这是唯一需要考虑预取的情况。 - Joe Fratianni
哦,天啊。我以为我有线程问题,所以东西加载不正确,这让我困扰了几个小时。谢谢! - Sami

1
根据Joe Fratianni的回答,这是由于预取造成的,可以将其禁用。当然,这也会失去预取的好处。
苹果在collectionView(_:cellForItemAt:)的文档中推荐的方法是在collectionView(_:willDisplay:forItemAt:)中更新单元格外观。
这样,单元格要么可见并且可以通过cellForItem(at:)进行即时更新,要么不可见但在滚动到视图中时获得最新信息。

另一个选择对我有用的是在保留一些预取优势的情况下,通过在performBatchUpdates块内使用cellForItem(at indexPath)调用来包装更新,例如:

class MyViewController: UIViewController {
    @IBOutlet weak var collectionView: UICollectionView!
    ...
    func someMethod() {
        ...
        collectionView.performBatchUpdates {
            ...
            if let cell = collectionView.cellForItem(at: indexPath) as? MyCell {
                // Update cell, e.g.:
                cell.someField = someValue
            }
            ...
        } completion: { _ in }
        ...
    }
    ...
}

显然,performBatchUpdates 调用会丢弃在 collectionView(_:cellForItemAt:) 中准备好但由于预取尚未可见的任何单元格(由于预取)。不幸的是,似乎这些被丢弃的单元格不会再次被预取,并且只有在它们变为可见时才会重新加载。因此,预取的某些优点会丢失。

0

确保您尝试访问的单元格可见,使用[self.collectionview cellForItemAtIndexPath: indexPath]

如果不可见,它将始终返回nil。

您应该访问数据源(数组、核心数据等)以获取在该单元格中显示的数据,而不是访问单元格本身。


谢谢。如果不可见,我期望会得到 nil,但奇怪的是,在 iOS 9 中运行时它可以完美地工作。此外,如果我添加一个标签属性,使其等于单元格的行,并使用以下方法检索单元格,则在 iOS 9 和 iOS 10 中它也可以完美地工作:CustomCollectionViewCell * customCell =(CustomCollectionViewCell *)[ self.collectionview viewWithTag:indexPath.row]; - Joe Fratianni
非常令人沮丧。如果我慢慢滚动,它会返回nil。也就是说,在加载图像时,单元格已经创建但尚未可见。如果我快速滚动到一个单元格,那么它就可以正常工作,即返回已加载的图像并显示单元格。 - Joe Fratianni

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