【问题标题】:iOS 10 collectionView:prefetchItemsAt not callediOS 10 collectionView:prefetchItemsAt 未调用
【发布时间】:2025-12-28 19:00:12
【问题描述】:

我正在尝试UICollectionViewDataSourcePrefetching 协议的新collectionView:prefetchItemsAtcollectionView:cancelPrefetchingForItemsAt 方法,但它们都没有被调用。委托是通过接口构建器设置的(从具有相同结果的代码中尝试),部署目标设置为 iOS 10,所以理论上它应该可以工作。所以我的问题是我是否遗漏了一些东西以使其工作,或者机制比我想象的更复杂,系统不认为它应该进行预取?

【问题讨论】:

  • 是否将名为prefetchingEnabledUICollectionView 属性设置为YES?刚刚在 Xcode 8.0 beta 4 中尝试过这个案例 - 按预期工作。
  • @RomanErmolov 是的,它被设置为true,但没有效果。仅供参考,我使用的是 Swift
  • 理论上,语言根本不重要。我可以猜测您没有不可见的单元格 - 如果所有单元格都在屏幕上,则不会调用此方法。
  • 最后一次尝试:您是否设置了prefetchDataSourceUICollectionView 属性?
  • 您找到解决方案了吗?似乎集合视图的预取仅在您的单元格具有固定大小时才有效。太糟糕了。

标签: ios uicollectionview ios10


【解决方案1】:

您可能正在设置estimatedItemSize,在这种情况下可能不会调用预取。

来源:在一个迷你演示应用中试用。

【讨论】:

  • 你拯救了我的一天,谢谢
  • yes 将其设置为 none 似乎会使回调被调用。
【解决方案2】:

在你的 ViewController 中实现 UICollectionViewDataSourcePrefetching 接口:

class ViewController: UIViewController, UICollectionViewDataSourcePrefetching {

  override func viewDidLoad() {
      super.viewDidLoad()

      collectionView.delegate = self
      collectionView.dataSource = self
      collectionView.prefetchDataSource = self
   }
}

您必须检查集合视图属性是否启用了“isPrefetching”。如果未启用,请将其值设置为 true。

collectionView.isPrefetchingEnabled = true  

【讨论】:

    【解决方案3】:

    来自docs

    集合视图不会立即为它需要的单元格调用此方法,因此您的代码不能依赖此方法来加载数据

    【讨论】:

      【解决方案4】:

      从您的ViewController 中的协议UICollectionViewDataSourcePrefetching 继承:

      class ViewController: UIViewController, UICollectionViewDataSourcePrefetching {
      

      在情节提要中将以下委托设置到您的集合视图(请参见附图),或者以编程方式进行。

      override func viewDidLoad() {
          super.viewDidLoad()
      
          collectionView.delegate = self
          collectionView.dataSource = self
          collectionView.prefetchDataSource = self
      }
      

      参考这个例子:https://github.com/Be-With-Viresh/CollectionViewWithPrefetch

      【讨论】:

        【解决方案5】:

        override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {

        //给这个函数添加代码

        self.collectionView(collectionView, prefetchItemsAt: [indexPath])

        }
        

        【讨论】:

          【解决方案6】:

          流式布局

          将估计大小设置为除none 之外的任何值会导致默认流布局不为我调用预取方法。这是一个示例回调:

          func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
              print(indexPaths.map { $0.row })
          }
          

          注意在这里设置这个,实际上是在隐式创建的流布局上设置的,而不是实际的UICollectionView

          这可以使用 Apple 的示例代码进行验证,将其更改为 none 以外的任何内容会导致它不调用委托。

          https://developer.apple.com/documentation/uikit/uicollectionviewdatasourceprefetching/prefetching_collection_view_data

          组合布局

          另一方面,它也可以与组合布局一起使用,因为它们没有 estimatedSize 属性,就像在 IB 中自动创建的默认流布局一样。如果您创建组合布局,它将替换默认的流布局,因此如果您设置组合布局,则在 IB 中设置该属性将不起作用。

          组合布局示例:

          func createCompositionalLayout() -> UICollectionViewLayout {
              let itemSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .fractionalHeight(1.0)
              )
              let item = NSCollectionLayoutItem(layoutSize: itemSize)
              let groupSize = NSCollectionLayoutSize(
                widthDimension: .fractionalWidth(1.0),
                heightDimension: .fractionalWidth(1/5)
              )
              let group = NSCollectionLayoutGroup.horizontal(
                layoutSize: groupSize,
                subitem: item,
                count: 1
              )
              let section = NSCollectionLayoutSection(group: group)
              return UICollectionViewCompositionalLayout(section: section)
          }
          

          然后这样设置:

          collectionView.collectionViewLayout = createCompositionalLayout()
          

          viewDidLoad 委托中。

          这样做确实允许调用prefetchItemsAt 方法。

          还发现,如果在组合布局上估计单元格高度,则它不起作用。这很糟糕。

          UICollectionView: compositional layout disables prefetching?

          似乎如果您的任何 layoutSize 对象具有估计尺寸,它就会在预取时退出。

              let size = NSCollectionLayoutSize(widthDimension: .estimated(104),
                                                heightDimension: .estimated(128))
              return NSCollectionLayoutItem(layoutSize: size)
          

          【讨论】:

            最近更新 更多