如何在TableView Cell中为耗时任务实时显示Progress Bar?
耗时任务中实时显示进度条的实现方案
问题背景
执行耗时任务时,如何用Progress Bar实时展示进度?比如某个执行时间长、完成后返回True的函数,执行期间怎么实现进度条实时更新?不确定是否需要用到线程,当前函数执行时无任何输出,希望从代码层面了解具体实现方式。
现有代码示例
func callApi() { let group = DispatchGroup() for (indexVal, ref) in arrayURL.enumerated() { group.enter() let progressQueue = DispatchQueue(label: "com.alamofire.progressQueue", qos: .utility) AF.download(ref) .downloadProgress(queue: progressQueue) { progress in print("Download Progress: \(progress.fractionCompleted)") DispatchQueue.main.async { guard let cell = self.tableProgress.cellForRow(at: NSIndexPath.init(row: indexVal, section: 0) as IndexPath) as? ProgressCell else { return // or fatalError() or whatever } cell.progressView.progress = Float(progress.fractionCompleted) } } .responseData { response in if let data = response.value { print("===============>", indexVal) group.leave() print(data) } } } group.notify(queue: .main, execute: { // executed after all async calls in for loop finish print("done with all async calls") }) }
核心实现思路与优化建议
1. 必须用异步线程/队列
耗时任务绝对不能放在主线程执行——主线程负责UI渲染,一旦被阻塞,界面会直接卡死,进度条根本无法实时更新。正确逻辑是:
- 把耗时任务(下载、本地计算、文件IO等)放到后台队列执行
- 所有更新UI的操作(包括进度条刷新)必须切回主线程执行
2. 你的下载任务代码解析
你基于Alamofire的代码已经做了正确的基础处理:
- 用
DispatchGroup管理多个下载任务的完成状态,确保所有任务结束后执行统一回调 - 给
downloadProgress指定后台队列progressQueue处理进度计算,避免占用主线程资源 - 进度回调中通过
DispatchQueue.main.async切回主线程更新Cell进度条,符合UI操作必须在主线程的规则
3. 代码优化点
避免直接通过
cellForRow获取Cell:当TableView滚动时,目标Cell可能被复用或滚出屏幕,cellForRow会返回nil,导致进度更新失效。建议改用数据驱动:- 初始化一个和
arrayURL长度一致的进度数组:var taskProgresses: [Float] = Array(repeating: 0.0, count: arrayURL.count) - 进度更新时,先修改数组值:
taskProgresses[indexVal] = Float(progress.fractionCompleted) - 调用TableView刷新对应行:
tableProgress.reloadRows(at: [IndexPath(row: indexVal, section: 0)], with: .none) - 在
ProgressCell中添加配置方法,比如func configure(with progress: Float),在cellForRowAt中调用该方法设置进度,适配Cell复用场景
- 初始化一个和
完善错误处理:当前代码仅在下载成功时调用
group.leave(),如果下载失败(网络错误、文件损坏等),会导致DispatchGroup永远处于等待状态,引发内存泄漏或逻辑阻塞。建议在responseData中无论成功失败都执行group.leave():.responseData { response in defer { group.leave() // 无论成功失败都执行leave } if let data = response.value { print("===============>", indexVal) print(data) } else { print("下载失败:\(indexVal)") } }
4. 通用耗时任务(非网络)的进度实现
如果是本地耗时任务(比如大文件解析、循环计算),可以通过闭包回调传递进度:
// 定义带进度回调的耗时任务函数 func longRunningTask(totalSteps: Int, completion: @escaping (Bool) -> Void, progressUpdate: @escaping (Float) -> Void) { // 放到后台队列执行 DispatchQueue.global(qos: .utility).async { for step in 0..<totalSteps { // 模拟耗时操作(替换成你的业务逻辑) Thread.sleep(forTimeInterval: 0.001) // 计算当前进度 let currentProgress = Float(step + 1) / Float(totalSteps) // 切回主线程更新UI DispatchQueue.main.async { progressUpdate(currentProgress) } } // 任务完成,返回结果 DispatchQueue.main.async { completion(true) } } } // 调用示例 longRunningTask(totalSteps: 1000, completion: { success in print("任务执行完成:\(success)") }, progressUpdate: { progress in // 更新UI进度条 self.globalProgressView.progress = progress })
内容的提问来源于stack exchange,提问作者mohan raj
相关产品推荐
相关产品推荐

