LPMetadataProvider是一次性对象,CollectionView滚动时报错如何解决?
问题根因
你遇到的报错核心原因是*LPMetadataProvider属于单次使用对象,完成一次元数据请求后就无法再次调用startFetchingMetadata方法*。而CollectionView的单元格会被滚动复用,每次重新赋值previewCell时都会触发fetchPreview,复用同一个全局初始化的provider就会触发报错:
Trying to start fetching on an LPMetadataProvider that has already started. LPMetadataProvider is a one-shot object.
解决方案
你可以按照以下逻辑修改代码,解决复用冲突同时保证能拿到didSet传递的链接参数:
var urlString: String? // 存储当前请求的链接,用于回调校验避免内容错位 var currentFetchURL: URL? // provider改为可选类型,方便重用时重置 var provider: LPMetadataProvider? var previewCell: LinkModel? { didSet { urlString = previewCell?.linkURL fetchPreview() } } let linkPreview = LPLinkView() override init(frame: CGRect) { super.init(frame: frame) contentView.backgroundColor = .black // LPLinkView只需要添加一次,不要在请求回调中重复添加 contentView.addSubview(linkPreview) // 适配布局,也可以用frame布局 linkPreview.frame = contentView.bounds linkPreview.autoresizingMask = [.flexibleWidth, .flexibleHeight] } // 重写重用方法,清理旧状态 override func prepareForReuse() { super.prepareForReuse() // 取消未完成的旧请求 provider?.cancel() provider = nil // 清空旧预览内容 linkPreview.metadata = nil currentFetchURL = nil } fileprivate func fetchPreview() { guard let url = URL(string: urlString ?? "") else { return } currentFetchURL = url // 每次请求创建全新的provider,避免单次使用限制 let newProvider = LPMetadataProvider() self.provider = newProvider newProvider.startFetchingMetadata(for: url) { [weak self] metaData, error in guard let self = self, let data = metaData, error == nil, // 校验链接一致性,避免滚动复用出现内容错位 self.currentFetchURL == url else { return } DispatchQueue.main.async { self.linkPreview.metadata = data } } }
核心修改点
- 每次触发
fetchPreview时都创建全新的LPMetadataProvider实例,绕过单次使用的限制 - 重写
prepareForReuse方法,在单元格被复用前取消未完成的旧请求、清空历史状态,避免旧请求返回后更新错误内容 - 将
LPLinkView的添加和布局逻辑移到初始化方法中,避免请求成功后重复添加视图到层级中 - 增加
currentFetchURL校验逻辑,确保回调返回的元数据属于当前单元格需要展示的链接,避免滚动时出现内容错位
内容的提问来源于stack exchange,提问作者SwiftTry
相关产品推荐
相关产品推荐

