【问题标题】:Preloading UITableView cells预加载 UITableView 单元格
【发布时间】:2012-09-22 09:00:22
【问题描述】:

我有一个 UITableView,它从数据库中检索一些数据,这些数据的预取成本相对较高。为了解决这个问题,我使用了LazyTableImages 示例代码中的设计理念,在滚动结束时使用[UITableView visibleCells] 获取可见行的数据。

这在性能方面效果很好,但它有两个我想解决的交互问题:

  1. 在滚动完全停止之前不会加载数据(即scrollViewDidEndDecelerating)。因此,如果我在列表中缓慢滚动,我必须停止滚动才能将数据加载到那些可见的单元格中。理想情况下,如果滚动速度较慢,我会将数据加载到单元格中。
  2. 我可能希望预取 1 或 2 个表格单元格数据,这些数据刚刚离开屏幕以期待滚动到它们。

有没有一种优雅的方式来实现这一点? 我感觉我可能缺少解决这个问题的通用设计模式。

【问题讨论】:

  • 你应该优化预取。
  • 我尝试过的一种方法是一次预加载 30 个单元格,但是如果用户滚动得太快,那么我最终会选择没有数据的单元格然后不加载.
  • 在 UITableView 的自定义子类中加载 UITableViewCell 异步的内容。加载完成后,通过返回主线程替换占位符文本。所以主线程(处理 UI 的东西)不会被阻塞太久,它会允许平滑滚动。

标签: ios uitableview


【解决方案1】:

我认为您可以在滚动开始时开始加载数据,这样您就不必等待滚动结束。您可以使用以下两种方法之一:

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{}

而且您始终可以获取可见单元格的索引,因此我认为您应该能够加载更多数据,而不仅仅是可见单元格。

【讨论】:

    【解决方案2】:

    如果您按原样使用 LazyTableImages 代码,这是正常的。 加载被推迟到滚动结束。 在我的测试应用程序(一个简单的类似 instagram 的流)中,我使用了不同的方法:基本上我将单元格子类化,从单元格本身开始下载,使用单元格作为我的下载管理器的代表,然后更新单元格的 UI下载完成时

    详细说明

    • 我在 cellForRowAtIndexPath 方法中开始下载(如果视图正在滚动,LazyTableImages 会延迟下载,我不这样做,因为对我来说可以在滚动时加载)调用我的单元格的“startDownload”方法。
    • 我的单元格子类被设置为我的下载管理器的委托,当下载完成时,单元格会用真实图像更改占位符(这是在自定义的“downloadManagerDidFinishDownloadForImage:(UIImage*)image”委托方法中完成的,在单元子类中实现)
    • 在我的单元子类中,我重写了 prepareForReuse 方法。在这里,如果要重复使用单元格,我可以选择停止下载。就我而言,我只是取消设置委托并继续下载,因为我有一些使用核心数据进行的图像缓存,我希望缓存继续进行。

    此外,您必须注意两件事:

    • 您的下载管理器需要在后台线程中工作
    • 请务必在主线程中更新您的单元 UI。您可以使用

    dispatch_async(dispatch_get_main_queue(), ^{
        //update here your cell UI
    });
    

    在委托方法中(如果该方法由您的下载管理器调用,它肯定会在后台线程中调用,因此您必须使用调度“在主线程中“移动”)。

    如果你愿意,你可以看看我的例子,但它是“原样”(它是一个测试应用程序,有很多意大利面条代码和与你的问题无关的东西 ;-)

    http://www.lombax.it/documents/InstaXplore.zip

    在我的示例中,您必须查看:

    • BrowseCell
    • BrowsePhotoViewController

    其他的东西都和核心数据存储和instagram有关,但基本上

    • 在 cellForRowAtIndexPath 中,我创建了一个新单元格(或重复使用以前的单元格)并传递一个 InstaImage 项(我的自定义图像)
    • InstaImage 检查是否存在缓存副本,如果不存在,则开始下载,然后提醒单元格

    你有一个清除缓存按钮可以一次又一次地尝试:-)

    【讨论】:

      【解决方案3】:
      // called on finger up if the user dragged. decelerate is true if it will continue moving afterwards
      - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
      
      
      
      (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
      {
          if (!decelerate)
          {
              //fetch data for visible rows
          }
      }
      

      【讨论】:

      • 这是来自 LazyTableImages 的示例,如前所述,它使单元格“卸载”,直到它们出现在屏幕上。我想预装它。
      【解决方案4】:

      如果我没有误解你的问题,我想你可以使用这个。您确实在 SOME 单元格显示时加载。

      -(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
      
      }
      

      【讨论】:

        【解决方案5】:

        Lazy Image 逻辑是一个很好的解决方案,我曾经在我的 tableviews 中使用,但最近我一直在为 UITableViews 实现不同的解决方案。首先是实现 NSOperation Queues,就像 Apple 在 WWWDC https://developer.apple.com/videos/wwdc/2012/?include=211#211987654321@ 中提供的视频中的那样。

        或者您可以尝试创建一个无限滚动,当您到达某个 uitableviewcell 时,您将加载一个包含更多数据的新“页面”,如果您需要有关此解决方案的帮助,请告诉我!

        希望这会有所帮助!

        最好的问候,

        豪尔赫。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-22
          • 2021-03-21
          • 1970-01-01
          • 2014-01-20
          相关资源
          最近更新 更多