You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免UITableViewCell滚动时更新内容(含下载进度显示场景)

我之前处理过不少UITableView里动态更新进度条的场景,用标签绑定的方式其实很容易踩坑——比如单元格复用的时候,标签会和下载项错位,导致进度条更新到错误的单元格上。给你几个更可靠的方案,都是实际项目里验证过的:

方案1:模型绑定(最推荐,从根源避免复用问题)

核心思路是把下载状态和进度直接存在对应的数据模型里,单元格根据模型状态自动更新:

  • 先给你的下载数据模型(比如叫DownloadItem)加两个属性:
    struct DownloadItem {
        let id: String
        var isDownloading: Bool = false
        var progress: Float = 0.0
        // 其他原有属性...
    }
    
  • 自定义MyCell时,添加一个配置方法,让单元格接收模型并更新UI:
    class MyCell: UITableViewCell {
        @IBOutlet weak var progressView: UIProgressView!
        @IBOutlet weak var downloadButton: UIButton!
        
        func configure(with item: DownloadItem) {
            // 根据模型状态控制进度条显示/隐藏
            progressView.isHidden = !item.isDownloading
            progressView.progress = item.progress
            // 按钮文字也可以跟着切换,比如下载中显示"暂停"
            downloadButton.setTitle(item.isDownloading ? "暂停" : "下载", for: .normal)
        }
    }
    
  • 在tableView(_:cellForRowAt:)里给单元格传模型:
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
        let item = downloadItems[indexPath.row]
        cell.configure(with: item)
        // 给下载按钮绑定点击事件,用indexPath.row关联对应的模型
        cell.downloadButton.tag = indexPath.row
        cell.downloadButton.addTarget(self, action: #selector(startDownload(_:)), for: .touchUpInside)
        return cell
    }
    
  • 下载进度更新时,直接修改对应模型的progress,然后刷新单元格:
    func updateDownloadProgress(for itemID: String, progress: Float) {
        guard let index = downloadItems.firstIndex(where: { $0.id == itemID }) else { return }
        downloadItems[index].progress = progress
        // 只刷新对应行,避免整个表格闪烁
        tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
    }
    
方案2:闭包回调(适合不想改动原有模型的场景)

如果暂时不想修改数据模型,可以用闭包让下载任务直接绑定到对应的单元格:

  • 在MyCell里添加一个进度更新的闭包属性:
    class MyCell: UITableViewCell {
        @IBOutlet weak var progressView: UIProgressView!
        // 进度更新回调
        var onProgressUpdate: ((Float) -> Void)?
        
        func showProgress() {
            progressView.isHidden = false
        }
    }
    
  • 在单元格配置时,把闭包和下载任务关联:
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
        let item = downloadItems[indexPath.row]
        cell.progressView.isHidden = true
        
        // 绑定闭包,让下载任务能直接更新这个单元格的进度
        cell.onProgressUpdate = { [weak cell] progress in
            DispatchQueue.main.async {
                cell?.progressView.progress = progress
            }
        }
        
        cell.downloadButton.tag = indexPath.row
        cell.downloadButton.addTarget(self, action: #selector(startDownload(_:)), for: .touchUpInside)
        return cell
    }
    
  • 启动下载任务时,拿到对应单元格的闭包来更新进度:
    @objc func startDownload(_ sender: UIButton) {
        let index = sender.tag
        let item = downloadItems[index]
        guard let cell = tableView.cellForRow(at: IndexPath(row: index, section: 0)) as? MyCell else { return }
        
        cell.showProgress()
        // 假设这是你的下载任务,进度回调里调用单元格的闭包
        let downloadTask = createDownloadTask(for: item) { progress in
            cell.onProgressUpdate?(progress)
        }
        // 保存任务,方便后续暂停/取消
        activeDownloads[item.id] = downloadTask
    }
    
为什么不推荐用标签绑定?

UITableView的单元格是复用机制,当你滚动表格时,旧单元格会被重新分配给新的行,这时候标签对应的下载项就会混乱,导致进度条更新到完全不相关的单元格上,排查起来特别麻烦。

内容的提问来源于stack exchange,提问作者beev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:21:03