如何在视图控制器外以编程方式创建UIProgressView?
这个问题我之前拆分网络逻辑和UI代码时也踩过坑!核心点在于UI组件的创建和更新必须由拥有视图层级的对象(比如视图控制器)来负责,NetworkHelper作为业务逻辑类,本身没有view属性,自然不能直接往上面加ProgressView。给你三个实用的解决方案,按需选择:
方案一:用闭包回调(最简洁的方式)
让NetworkHelper通过闭包把进度变化传递给视图控制器,由VC来负责创建和更新ProgressView:
// NetworkHelper类定义 class NetworkHelper { // 定义进度回调闭包类型 typealias ProgressHandler = (Float) -> Void var progressHandler: ProgressHandler? func startDownload(with url: URL) { let task = URLSession.shared.downloadTask(with: url) { _, _, _ in // 下载完成后的逻辑处理 } task.resume() // 监听下载进度 task.progress.addObserver(self, forKeyPath: "fractionCompleted", options: .new, context: nil) } // 监听进度变化 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "fractionCompleted", let progress = object as? Progress else { return } let progressValue = Float(progress.fractionCompleted) // 回到主线程传递进度(UI操作必须在主线程) DispatchQueue.main.async { self.progressHandler?(progressValue) } } // 销毁时移除观察者,避免内存泄漏 deinit { task.progress.removeObserver(self, forKeyPath: "fractionCompleted") } } // 在你的视图控制器中使用 class DownloadVC: UIViewController { private let progressView = UIProgressView(progressViewStyle: .default) private let networkHelper = NetworkHelper() override func viewDidLoad() { super.viewDidLoad() // 配置并添加ProgressView到视图层级 progressView.frame = CGRect(x: 20, y: 100, width: view.bounds.width - 40, height: 20) view.addSubview(progressView) // 设置闭包接收进度更新 networkHelper.progressHandler = { [weak self] progress in self?.progressView.setProgress(progress, animated: true) } // 启动下载 guard let url = URL(string: "your_download_url") else { return } networkHelper.startDownload(with: url) } }
方案二:代理模式(适合复杂交互场景)
定义一个代理协议,让视图控制器遵守协议,NetworkHelper通过代理方法传递进度:
// 定义进度代理协议 protocol NetworkProgressDelegate: AnyObject { func networkHelper(_ helper: NetworkHelper, didUpdateProgress progress: Float) } class NetworkHelper { weak var delegate: NetworkProgressDelegate? func startDownload(with url: URL) { let task = URLSession.shared.downloadTask(with: url) { _, _, _ in // 下载完成逻辑 } task.resume() task.progress.addObserver(self, forKeyPath: "fractionCompleted", options: .new, context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "fractionCompleted", let progress = object as? Progress else { return } let progressValue = Float(progress.fractionCompleted) DispatchQueue.main.async { self.delegate?.networkHelper(self, didUpdateProgress: progressValue) } } deinit { task.progress.removeObserver(self, forKeyPath: "fractionCompleted") } } // 视图控制器遵守代理协议 class DownloadVC: UIViewController, NetworkProgressDelegate { private let progressView = UIProgressView(progressViewStyle: .default) private let networkHelper = NetworkHelper() override func viewDidLoad() { super.viewDidLoad() // 配置ProgressView progressView.frame = CGRect(x: 20, y: 100, width: view.bounds.width - 40, height: 20) view.addSubview(progressView) // 设置代理 networkHelper.delegate = self // 启动下载 guard let url = URL(string: "your_download_url") else { return } networkHelper.startDownload(with: url) } // 实现代理方法更新进度 func networkHelper(_ helper: NetworkHelper, didUpdateProgress progress: Float) { progressView.setProgress(progress, animated: true) } }
方案三:NotificationCenter(多组件需监听进度时使用)
通过发送通知的方式传递进度,适合多个视图或组件需要监听同一份网络进度的场景:
// 定义通知名称 extension Notification.Name { static let networkProgressUpdated = Notification.Name("NetworkProgressUpdated") } class NetworkHelper { func startDownload(with url: URL) { let task = URLSession.shared.downloadTask(with: url) { _, _, _ in // 下载完成逻辑 } task.resume() task.progress.addObserver(self, forKeyPath: "fractionCompleted", options: .new, context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "fractionCompleted", let progress = object as? Progress else { return } let progressValue = Float(progress.fractionCompleted) DispatchQueue.main.async { // 发送通知携带进度值 NotificationCenter.default.post( name: .networkProgressUpdated, object: nil, userInfo: ["progress": progressValue] ) } } deinit { task.progress.removeObserver(self, forKeyPath: "fractionCompleted") } } // 视图控制器中监听通知 class DownloadVC: UIViewController { private let progressView = UIProgressView(progressViewStyle: .default) override func viewDidLoad() { super.viewDidLoad() // 配置ProgressView progressView.frame = CGRect(x: 20, y: 100, width: view.bounds.width - 40, height: 20) view.addSubview(progressView) // 监听进度通知 NotificationCenter.default.addObserver( self, selector: #selector(handleProgressUpdate(_:)), name: .networkProgressUpdated, object: nil ) // 启动下载 guard let url = URL(string: "your_download_url") else { return } let networkHelper = NetworkHelper() networkHelper.startDownload(with: url) } @objc private func handleProgressUpdate(_ notification: Notification) { guard let progress = notification.userInfo?["progress"] as? Float else { return } progressView.setProgress(progress, animated: true) } // 销毁时移除通知观察者 deinit { NotificationCenter.default.removeObserver(self) } }
总的来说,闭包和代理是更推荐的方式,耦合度更低、代码可读性更好。不管用哪种方案,一定要记住所有UI操作必须放在主线程执行,也就是用DispatchQueue.main.async包裹,否则会出现UI不更新甚至崩溃的情况。
内容的提问来源于stack exchange,提问作者SwiftyJD
相关产品推荐
相关产品推荐

