【问题标题】:What is the proper method/accepted standard for loading data asynchronously into table view cells?将数据异步加载到表格视图单元格中的正确方法/公认标准是什么?
【发布时间】:2014-02-25 13:52:28
【问题描述】:

TweetbotTwitterrificAlien Blue 等许多显示来自 API 的图像的应用程序似乎以异步方式加载它们。似乎初始可见图像是同步加载的——也就是说,在它们的图像准备好之前不会显示初始可见单元格——但是当进一步向下滚动超过初始可见单元格时,似乎这些新单元格的图像是独立于初始同步加载。

例如,当用户在 Twitter 应用程序中加载推文时,最初可见的六七条推文在加载用户头像的图像之前不会显示,但之后的 40 多条推文 (直到用户滚动才可见),我们不必等待这些图像已加载以显示初始单元格,因为它们甚至还不可见。似乎大多数应用程序都允许这些图像独立加载,从而可以更快地显示初始推文。

我很困惑如何最好地完成此任务,例如,如果大多数开发人员执行此操作的标准方式我不知道。

我习惯于通过从响应中向 Core Data 提供对象来使用 Core Data,然后NSFetchedResultsController 在表格视图中显示它们。但是,这具有负面影响,即要求所有内容都已完全加载并存储在对象中,然后才能显示单元格。这意味着如果 API 返回一个图片链接给我并且我想在单元格中显示该图片,我必须将链接加载到 UIImage 并将其添加到 Core Data。

所以我的问题基本上归结为: Tweetbot、Twitterrific、Alien Blue 等流行应用程序如何处理需要加载大量图像和数据的表格视图,但仍然允许非常快速的加载,无需加载不必要的数据,并且仍然保持高滚动性能?

【问题讨论】:

  • @user716216 这不能回答我的问题。我需要知道如何将它与 Core Data 等持久存储结合使用,如何仅处理初始可查看单元格的加载等。这个问题需要的不仅仅是一个链接。
  • 这就是为什么我将其发布为评论,而不是答案。
  • 好的,谢谢。我知道 AFNetworking 对于 UIImageView 也有类似的类别,我只是不确定它是否正是我正在寻找的。​​span>

标签: ios objective-c uitableview core-data grand-central-dispatch


【解决方案1】:

没有完美的答案。这是一种艺术形式,可以改进和调整所涉及的各个部分,从而为您的应用程序提供最佳用户体验。不过,归根结底,您所描述的内容涉及以下内容的熟练组合:

延迟加载

对于延迟加载,我最喜欢的 goto 是 SDWebImage。为了达到您描述的效果,前 6 或 7 条推文在图像出现之前不会加载,您可以最初隐藏推文并使用 SDWebImage 的完成块取消隐藏它们。

分页

分页是指最初检索一组对象并在用户到达它们之前异步拉下下 X 个对象的做法(通常由滚动启动)。您还可以将此扩展为仅在用户滚动变慢或停止时才加载图像,这样您就不会浪费时间加载用户滚动过去的图像。 GitHub 上有一些很好的示例,例如 NMPaginator,或者您可以使用 UIScrollViewDelegate 方法自行开发。

设置您的滚动视图委托并实施:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
  if (!decelerate) {
    [self loadImagesForVisibleRows];
  }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
  [self loadImagesForVisibleRows];
}

然后添加一个loadImagesForVisibleRows 方法,使用[self.tableView indexPathsForVisibleRows]; 访问可见行并延迟加载这些行的图像。

高效载荷

最后,如果您保持负载干净、轻便,可以节省一些加载时间,尤其是对于网络连接较弱的用户。我会推荐 Vinay 的 Sahni 的博文 Best Practices for Designing a Pragmatic RESTful API,以获得有关 RESTful API 设计和分页的大量信息。

欢迎对上述内容提出任何建议或补充。

【讨论】:

    【解决方案2】:

    Apple 文档中有一些 sample code 演示了将图像延迟加载到表格视图单元格中的方法。它负责取消滚动到视线之外的图像的下载,并且在用户停止滚动之前根本不开始图像下载。

    【讨论】:

      【解决方案3】:

      有很多解决方案,你可以在这里找到很多关于堆栈溢出的解决方案。

      基本的想法是你想在一个块或NSOperation 中加载图像。我更喜欢NSOperation,因为您可以取消它们,这在单元格滚动到屏幕外时很有用。

      我推荐一个控制器来管理您的图像队列。您的表格单元格从控制器请求图像,如果控制器有图像,它会立即返回。如果没有,则返回 nil 并获取图像。

      从这里有选项。您可以在检索图像时使用块进行回调。我不喜欢它,但它很受欢迎。我更喜欢在接收到图像时使用NSNotification,并且单元格侦听NSNotification 来填充。

      我在 github 上有一个相当复杂的示例,可以处理这个问题以及更多其他问题。它在我的共享存储库http://github.com/ZarraStudios/ZDS_Shared 中。主类叫ZSAssetManager

      注意:这个示例代码已经有几年的历史了,所以它还没有准备好ARC,需要一些调整。它也可能比您正在寻找的更复杂,因为它还处理带宽检测和反应。然而,它向您展示了生产/高质量应用程序如何处理图像的异步加载。

      享受吧。

      【讨论】:

      • 嗨,马库斯,感谢您链接 ZSAssetManager 代码!我从那门课上学到了很多东西,很棒的东西。
      • 感谢这篇文章,我已经阅读了很多,我现在正在实施一些东西。不过我很好奇,为什么要使用单独的控制器进行缓存而不是 NSCache
      • NSCache 不能像大多数人期望的那样工作,截至上次测试,在 iOS 7 中根本不能工作。
      【解决方案4】:

      我一直在使用 dispatch_async 将数据异步加载到我的表格视图单元格中:

      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
      dispatch_async(queue, ^{
          NSData *imageData = [NSData dataWithContentsOfURL:url];
          dispatch_async(dispatch_get_main_queue(), ^{
              UIImage *image = [UIImage imageWithData:imageData];
              [imageView setImage:image];
          });
      });
      

      我在这个例子中使用的图像只有几千字节(每个),它们可以很好地加载到表格视图中。

      【讨论】:

      • 这是一个不好的做法,因为当图像返回时,单元格可能已经被重复使用了。这可能会导致图片出现在错误的单元格中。
      • 另外,如果您有很多行(例如在 Twitter 应用程序中),并且用户滚动速度非常快,这将同时启动大量请求,而您不需要。
      • 而且设备在阻塞之前可以进行有限数量的并发 HTTP 调用,在这种情况下,GCD 将增加底层线程的数量并创建更多阻塞的任务。 cell.tag=indexPath.rowif (cell.tag==indexPath.row) 只能在单元格仍然相同的情况下处理设置图像。
      • 并且您想在单元格离开屏幕时取消它们,这样您就不会下载不需要的东西。这甚至不涉及缓存...
      • @javadog36 如果您对单元重用没有问题,那么您可能至少有两个问题......
      猜你喜欢
      • 1970-01-01
      • 2015-03-30
      • 1970-01-01
      • 2020-08-29
      • 1970-01-01
      • 1970-01-01
      • 2018-07-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多