UICollectionView的prefetchItemsAt方法没有被调用

5
我正在尝试使用UICollectionViewDataSourcePrefetching协议为我的UICollectionView实现数据预取,但是相关方法没有被调用。
该集合视图有一个自定义布局。我也尝试了常规的流布局,结果相同。
在数据重新加载时,我执行以下代码,使集合视图具有其内容的大小,以防止在集合视图内部滚动但不在集合视图外部的滚动视图中发生滚动:
func reloadData() {
    viewDidLayoutSubviews()
    collectionView.reloadData()
    collectionView.layoutIfNeeded()
    viewDidLayoutSubviews()
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    collectionViewHeightConstraint.constant = collectionView.collectionViewLayout.collectionViewContentSize.height
    collectionView.layoutIfNeeded()
    view.layoutIfNeeded()
}

也许这与它有关?
我所做的事情:
- 我的UIViewController确实继承自UICollectionViewDataSourcePrefetching。 - collectionView.prefetchDataSource = self(也尝试使用storyboard)。 - collectionView.isPrefetchingEnabled = true(也尝试使用storyboard)。 - 我已经实现了collectionView(_:prefetchItemsAt:)。
问题: prefetchItemsAt方法没有被调用。我通过在该方法中放置一个print语句并设置断点来确定这一点。
2个回答

2

根据评论中的要求,我将在此分享我的实现方式:

我创建了名为prefetchState的跟踪器,用于确定当前是否正在进行预取操作:

enum PrefetchState {
    case fetching
    case idle
}
var prefetchState: PrefetchState = .idle

然后,我将滚动视图的代理(我的集合视图所在的滚动视图)连接到我的视图控制器,并实现了scrollViewDidScroll方法:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    guard scrollView == self.scrollView else { return }

    let prefetchThreshold: CGFloat = 100 // prefetching will start 100pts above the bottom of the scroll view
    if scrollView.contentOffset.y > scrollView.contentSize.height-screenBounds.height-prefetchThreshold {
        if prefetchState == .idle {
            prefetchItems()
        }
    }
}

在这里,您可以看到我检查是否已经预取。如果没有,我调用prefetchItems(),如下所示:

func prefetchItems() {
    guard prefetchState == .idle else { return }
    prefetchState = .fetching
    someDownloadFuncWithCompletionBlock { (newItems) in
        self.dataSource += newItems
        self.collectionView.reloadData()
        self.prefetchState = .idle
    }
}

感谢详细的解释。我的问题是在函数someDownloadFuncWithCompletionBlock中我们应该请求多少个项目?如果用户更改滚动方向,是否需要进行任何更改? - Lê Khánh Vinh
那就看你自己了。你可以只使用固定数量的项目,这取决于你的单元格有多大。对我来说,我一次屏幕上最多显示6个单元格。因此,我预取了12个,因为这样可以给我两页,而不会预取太多导致加载时间更长和数据消耗更多。 - LinusGeffarth
如果项目来自具有分页的API,并且当CollectionView到达底部时几乎同时发生预取,从而触发loadmore函数。在这种情况下,我们的预取可能会超过现有项目。 - Lê Khánh Vinh
我不确定你的意思是什么。在我的情况下,实际上就是这样发生的。我只需跟踪页面编号,并在每次调用“prefetchItems()”函数时将其递增。 - LinusGeffarth
1
至少感谢有一个解决方案,直到苹果决定支持自动调整大小单元格预取。 - Lê Khánh Vinh

1
我执行以下代码,使集合视图的大小与其内容相同,以防止在该集合视图外的滚动视图中滚动。但这听起来非常糟糕。你为什么要这样做?从文档中可以看到,集合视图上的预取是在用户滚动集合视图时触发的。通过使集合视图框架与内容大小相同,您实际上禁用了集合视图本身的滚动。如果您强制集合视图框架与内容大小相同,则完全破坏了UICollectionView。预取未被调用的原因是每个单元格都已经加载完毕。没有任何东西需要预取了,因为您的集合视图同时显示了每个单元格。如果您想滚动集合视图...让集合视图处理它。您不应该需要将集合视图放在另一个滚动视图中。

感谢您指出第二部分。我已经意识到willDisplay:在视图加载时对所有单元格进行调用。禁用集合视图的调整大小后,情况就不再是这样了。预取也是如此。您有什么建议,我如何同时拥有集合视图的调整大小和预取? - LinusGeffarth
有什么建议可以同时实现集合视图的调整大小和预取吗?不要调整集合视图的大小。集合视图框架不应该比屏幕更大。 - Fogmeister
1
如果你想要预取数据...不要破坏集合视图。 :D 这就是全部了。或者再从设计角度重新审视你的解决方案... - Fogmeister
嗨@LinusGeffarth,你介意分享你的解决方案吗?我也在寻找一种与自适应大小的集合视图一起预取的方法。似乎只能使用固定大小的单元格。 - Lê Khánh Vinh
@LêKhánhVinh,我已经在下面发布了我的解决方案。 - LinusGeffarth
显示剩余4条评论

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