我有一个非常简单的UICollectionView
,它使用组合布局来轻松实现动态单元格高度。不幸的是,这样做似乎禁用了使用UICollectionViewDataSourcePrefetching
的内容预取。在下面的示例代码中,collectionView(_:prefetchItemsAt:)
方法只在集合视图的初始显示时调用一次。没有滚动操作会导致进一步调用该方法。
要使预取正常工作,我可以做些什么?
class ViewController: UIViewController,
UICollectionViewDataSource,
UICollectionViewDelegate,
UICollectionViewDataSourcePrefetching
{
@IBOutlet var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
collectionView.collectionViewLayout = createLayout()
collectionView.dataSource = self
collectionView.delegate = self
collectionView.prefetchDataSource = self
collectionView.register(MyCell.self, forCellWithReuseIdentifier: "cell")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 100
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCell
cell.label.text = String(repeating: "\(indexPath) ", count: indexPath.item)
return cell
}
// this is only called once. Why?
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
print("prefetch for \(indexPaths)")
}
private func createLayout() -> UICollectionViewLayout {
let layout = UICollectionViewCompositionalLayout { (_, _) -> NSCollectionLayoutSection? in
let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(44))
let item = NSCollectionLayoutItem(layoutSize: size)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitem: item, count: 1)
group.interItemSpacing = .fixed(16)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
section.interGroupSpacing = 8
return section
}
return layout
}
}
class MyCell: UICollectionViewCell {
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
backgroundColor = .orange
contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
label.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
(使用Xcode 11.5 / iOS 13.5。collectionView
插座连接到故事板中的全屏示例)
编辑:进一步的测试表明heightDimension: .estimated(44)
似乎是原因。用.absolute(44)
替换它使预取再次工作,但这当然违背了拥有这样一个多行文本布局的目的。看起来像一个bug,如果有人想复制它,我已经提交了FB 7849272。
EDIT/2:目前,我可以通过使用普通的流布局来避免这种情况,并计算每个单元格的高度,这也使预取再次工作。然而,我很好奇,如果没有一个变通方案,同时仍然使用组合布局,所以我增加了奖金。
4条答案
按热度按时间xfb7svmp1#
是的..!!没有一种方法可以对动态大小的单元格使用预取,您必须使用
collectionView willDisplay forItemAt
3htmauhk2#
我刚刚收到苹果的反馈,要求我在Xcode 13.1中重新测试这个问题,你瞧,这个bug确实已经被修复了。
pzfprimi3#
在iOS15.5上,预取工作正常,但在14.5上,正如你提到的,它被CompositionalLayout破坏了。
它只执行一次(当第一批可见单元格加载时),然后不会再次调用prefetch-delegate。
有一件事要提一下,在苹果的文档中,它并不保证对每个可能的indexPath都能运行。他们“建议”你实现一个基于操作的预取系统。每个indexPath都有一个获取图像的操作。换句话说,它是坏的,他们不在乎。
您可能还注意到,如果您创建了一个全屏宽度的单元格,并且禁用了滚动(意味着只有1个单元格),SDK将示例化2个单元格(其中一个永远不可见,也不会显示在视图层次结构中)。
我发誓iOS 14被诅咒了。
kh212irz4#
我和你遇到了同样的问题,但我设法解决了。我的布局尺寸是用分数计算的,而不是估计的:
并且,在委托方法prefetchItemsAt中,可以使用indexPaths计算需要在服务器上请求的偏移量。