如何在UITableViewCell中通过URL预览元数据 解决滚动内容错位问题
问题解决方案:UITableView使用LinkPreview框架滚动错位
错位核心原因
- 异步元数据请求未做绑定校验,请求返回时cell已经被复用来展示其他URL,旧请求返回后直接赋值错误的元数据
- 每次请求成功都重复调用
addSubview添加LPLinkView,cell复用多次后会叠加多层视图,显示混乱 - 没有实现cell复用的状态重置逻辑,旧的预览内容和未完成的请求没有在复用时清理
代码修改方案
1. UITableViewCell 代码修改
var urlString: String? var previewCell: LinkModel? { didSet { // 赋值新数据前先取消旧请求,清空旧预览 provideer.cancel() linkPreview.metadata = nil urlString = previewCell?.linkURL fetchPreview() } } let linkPreview = LPLinkView() var provideer = LPMetadataProvider() override init(frame: CGRect) { super.init(frame: frame) contentView.backgroundColor = .black // 初始化时一次性添加linkPreview,不要每次请求成功重复添加 contentView.addSubview(linkPreview) // 用自动布局适配大小,避免frame计算错误 linkPreview.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ linkPreview.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), linkPreview.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), linkPreview.topAnchor.constraint(equalTo: contentView.topAnchor), linkPreview.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } // 实现复用前的状态清理方法 override func prepareForReuse() { super.prepareForReuse() provideer.cancel() linkPreview.metadata = nil urlString = nil } fileprivate func fetchPreview() { guard let currentUrl = URL(string: urlString ?? "") else { return } provideer = LPMetadataProvider() provideer.startFetchingMetadata(for: currentUrl) { [weak self] metaData, error in guard let self = self, let data = metaData, error == nil, // 核心校验:返回的元数据对应的URL和当前cell需要展示的URL一致才赋值 data.url == currentUrl else { return } DispatchQueue.main.async { self.linkPreview.metadata = data } } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
2. 可选优化点
- 如果LinkModel存储的URL和元数据返回的URL存在格式差异(比如有无http前缀、query参数差异),可以把校验逻辑替换为字符串比对:
self.urlString == previewCell?.linkURL - 可以给LPLinkView添加加载占位背景,优化加载过程中的视觉体验
cellForRowAt方法不需要修改
内容的提问来源于stack exchange,提问作者SwiftTry
相关产品推荐
相关产品推荐

