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

Swift:如何更新另一UIViewController中的进度视图?上传场景求优化方案

嘿,针对你这个嵌入在UINavigationController里的UITableViewController上传文件、需要展示进度的场景,我给你梳理几个实用方案,绝对比单例靠谱多了~

先聊聊更友好的进度展示方案

其实自定义弹窗不是唯一选择,甚至未必是最优解,给你几个更贴合iOS用户习惯的选项:

  • 导航栏内嵌进度条:直接在UINavigationBar里加一个UIProgressView,可以替换titleView或者放在导航栏底部,上传时实时更新进度。这种方式不会遮挡表格内容,用户操作更顺畅,上传完成后可以自动隐藏,体验很自然。
  • 底部悬浮进度条:类似系统App Store下载的样式,在屏幕底部放一个窄条进度条,上传时滑入,完成后自动滑出。既不干扰主界面操作,又能让用户随时看到进度。
  • 轻量化弹窗(如果一定要用):别用全屏的自定义UIViewController,可以用UIAlertController嵌入UIProgressView,或者做一个半透明的小弹窗(模态样式设为.overCurrentContext),避免完全遮挡表格,减少用户的压迫感。
再解决控制器间连续数据传递的问题(替代Singleton)

单例确实坑不少——全局状态容易冲突(比如同时传多个上传进度)、内存泄漏风险(单例强引用弹窗导致无法释放)、耦合性太高。给你几个更合适的方案:

方案1:闭包(Closure)——最简洁的方式

在你的自定义弹窗控制器里定义一个进度更新的闭包,当表格控制器弹出弹窗时,把进度更新的逻辑传给它,上传过程中直接调用闭包更新UI。

示例代码:

自定义弹窗控制器

class ProgressPopupVC: UIViewController {
    private let progressView = UIProgressView(progressViewStyle: .default)
    
    // 定义进度更新闭包
    var onProgressUpdate: ((Float) -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 配置弹窗UI
        view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        progressView.frame = CGRect(x: 20, y: view.center.y, width: view.frame.width - 40, height: 8)
        view.addSubview(progressView)
    }
    
    // 对外暴露更新进度的方法(可选,也可以直接调用闭包)
    func updateProgress(_ progress: Float) {
        DispatchQueue.main.async {
            self.progressView.progress = progress
        }
    }
}

UITableViewController里的调用

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    
    // 初始化弹窗并设置模态样式
    let popupVC = ProgressPopupVC()
    popupVC.modalPresentationStyle = .overCurrentContext
    
    // 初始化上传任务
    let uploadTask = URLSession.shared.uploadTask(with: yourRequest, fromFile: yourFileURL)
    // 监听上传进度
    uploadTask.progress.addObserver(self, forKeyPath: "fractionCompleted", options: .new, context: nil)
    
    // 绑定闭包:上传进度变化时通知弹窗
    popupVC.onProgressUpdate = { [weak popupVC] progress in
        popupVC?.updateProgress(progress)
    }
    
    // 弹窗弹出后启动上传
    present(popupVC, animated: true) {
        uploadTask.resume()
    }
}

// KVO回调处理进度更新
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard keyPath == "fractionCompleted", let progress = object as? Progress else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    // 调用闭包更新弹窗进度
    (presentedViewController as? ProgressPopupVC)?.onProgressUpdate?(Float(progress.fractionCompleted))
}

方案2:代理模式(Delegate)——更规范的iOS传统写法

如果你的弹窗有更多交互(比如取消上传),代理模式会比闭包更清晰:

  1. 定义一个进度更新的协议:
protocol UploadProgressDelegate: AnyObject {
    func updateUploadProgress(_ progress: Float)
}
  1. 在弹窗控制器里声明代理属性:weak var delegate: UploadProgressDelegate?
  2. 在UITableViewController里遵守协议,实现updateUploadProgress方法,上传时把弹窗的delegate设为self,进度变化时调用delegate?.updateUploadProgress(...)

方案3:Combine框架(iOS 13+)——响应式编程更优雅

如果你的项目支持iOS 13及以上,用Combine的PassthroughSubject发送进度事件,弹窗订阅事件更新UI:

  • 在表格控制器里定义let progressSubject = PassthroughSubject<Float, Never>()
  • 弹窗初始化时订阅这个subject:progressSubject.sink { [weak self] progress in self?.progressView.progress = progress }.store(in: &cancellables)
  • 上传进度变化时发送事件:progressSubject.send(Float(progress.fractionCompleted))
为什么不推荐Singleton?

单例是全局共享的状态,如果你同时发起多个上传任务,进度会互相覆盖;而且单例如果持有弹窗的强引用,即使你dismiss了弹窗,它也不会被释放,容易造成内存泄漏。这种和特定任务绑定的临时数据传递,用上面的几种方案都比单例更合适。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:30:48