【问题标题】:UIcollectionView cellForItemAtIndexPath returns Null in iOS 10 only. Works fine in iOS 9 and iOS 8UIcollectionView cellForItemAtIndexPath 仅在 iOS 10 中返回 Null。在 iOS 9 和 iOS 8 中运行良好
【发布时间】:2017-03-12 09:41:22
【问题描述】:

我有一个应用已经发布了好几年。它在 UICollectionView 中检索 RSS 提要。 cellForItemAtIndexPath 方法设置文本并调用数据源方法以从提要中指定的链接加载图像。如果不存在,它会加载网页数据并搜索<img> 标签以获取图像。一旦找到/加载图像,就会调用委托方法将图像添加到单元格中。 (下)

在 iOS 8 和 9 中运行时一切正常,但在 iOS 10 中运行时,可见单元格会在最初加载 RSS 提要时使用图像进行更新,但在滚动时没有添加图像,并且我从 cellForItemAtIndexPath 得到 NULL。

当我向后滚动时会显示图像,如果我将 reloadItemsAtIndexPaths 添加到 imageWasLoadedForStory 但 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];
                });
        });
}

【问题讨论】:

  • 好吧,我可能没疯。我已经能够通过使用 tag 属性让它工作。在 cellForItemAtIndexPath 我添加了: [cell setTag: indexPath.row];并在我的委托方法中添加: CustomCollectionViewCell *customCell = (CustomCollectionViewCell *) [self.collectionview viewWithTag: indexPath.row];这使一切正常,但我认为这不是处理此问题的监管方式。

标签: ios uicollectionview uicollectionviewcell xcode8 ios10


【解决方案1】:

我鼓励大家阅读 Prefetching - iOS 10 中的新功能。简单的解决方案如下:

[self.customCollectionview setPrefetchingEnabled:NO];

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

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

Getting loaded vs visible cells on a UITableView or UICollectionView

【讨论】:

  • 非常感谢乔 :)。
  • 不幸的是,您的解决方案不适用于我的情况。我在viewDidLoad() 中设置了self.collectionView.isPrefetchingEnabled = false,但是当我在didDeselectItemAt indexPath: 中调用cellForItem(at:) 时,我仍然得到nil 返回。
  • 您所描述的内容肯定有其他问题。 (即错误的 indexPath 等)如果有人正在选择一个单元格,则必须加载它。应用预取的唯一时间是在显示单元格之前。即用户正在向下滚动并且单元格 2 到 8 可见。在 iOS 10 中,预加载了 9 到 15 个单元格,以期变得可见。这是唯一一次预取是一个问题。
  • 天哪。几个小时以来,我一直以为我遇到了线程问题,这就是为什么事情没有正确加载的原因。谢谢!
【解决方案2】:

根据Joe Fratianni's answer,这是由于预取,可以禁用。当然,这也失去了预取的好处。

Apple 在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:) 中准备但尚不可见(由于预取)的单元格。

不幸的是,那些丢弃的单元格似乎没有再次预取,并且只有在它们变得可见时才会重新加载。所以预取的一些好处就丢失了。

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 谢谢。如果不可见,我希望为零,但奇怪的是,如果我在 iOS 9 中运行它可以完美运行。此外,如果我添加一个等于单元格行的标签属性并使用检索单元格,它在 iOS 9 和 iOS 10 中可以完美运行: CustomCollectionViewCell *customCell = (CustomCollectionViewCell *) [self.collectionview viewWithTag: indexPath.row];
    • 令人沮丧。如果我慢慢滚动,则返回 nil。即单元格已创建但在加载图像时尚不可见。如果我快速滚动到一个单元格,那么它就可以工作,即返回加载的图像并且单元格是可见的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-22
    • 2017-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-01
    • 1970-01-01
    相关资源
    最近更新 更多