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

返回导航控制器后重载视图控制器,下载状态消失问题

Hey Noah, let's tackle that download state disappearing issue you're facing! The core problem here is that when you pop back and reload your view controller, the original instance (and its references to the download task/UI state) gets destroyed. But since the background URLSession keeps the task alive, we just need to decouple the download management from the view controller itself.

Here's a step-by-step fix:

1. Move Download Logic to a Singleton Manager

Create a shared singleton class to handle all download tasks, URLSession setup, and state tracking. This way, the download context survives even when your view controller is recreated.

class DownloadManager: NSObject, URLSessionDownloadDelegate {
    static let shared = DownloadManager()
    
    private var session: URLSession!
    private var activeTasks: [URL: URLSessionDownloadTask] = [:]
    
    // Closures to notify listeners of progress/completion
    var onProgressUpdate: ((URL, Double) -> Void)?
    var onDownloadComplete: ((URL, URL?, Error?) -> Void)?
    
    private override init() {
        super.init()
        // Use a background session to keep tasks running even if the app is in background
        let config = URLSessionConfiguration.background(withIdentifier: "com.yourapp.background.downloads")
        session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }
    
    // Start or resume a download task
    func startDownload(for url: URL) {
        guard activeTasks[url] == nil else {
            // Task already exists, maybe just resume it if paused
            activeTasks[url]?.resume()
            return
        }
        let task = session.downloadTask(with: url)
        activeTasks[url] = task
        task.resume()
    }
    
    // Get current progress for a specific download URL
    func getCurrentProgress(for url: URL) -> Double? {
        guard let task = activeTasks[url],
              let httpResponse = task.response as? HTTPURLResponse,
              httpResponse.expectedContentLength > 0 else {
            return nil
        }
        return Double(task.countOfBytesReceived) / Double(httpResponse.expectedContentLength)
    }
    
    // MARK: - URLSessionDownloadDelegate
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
        guard let url = downloadTask.originalRequest?.url else { return }
        let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
        // Notify listeners on main thread for UI updates
        DispatchQueue.main.async {
            self.onProgressUpdate?(url, progress)
        }
    }
    
    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        guard let url = downloadTask.originalRequest?.url else { return }
        activeTasks.removeValue(forKey: url)
        DispatchQueue.main.async {
            self.onDownloadComplete?(url, location, nil)
        }
    }
    
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        guard let url = task.originalRequest?.url else { return }
        activeTasks.removeValue(forKey: url)
        DispatchQueue.main.async {
            self.onDownloadComplete?(url, nil, error)
        }
    }
}

2. Update Your View Controller to Use the Singleton

Now, in your view controller, instead of managing the download task directly, we'll hook into the singleton to track state and update the UI whenever the view appears:

class YourDownloadVC: UIViewController {
    @IBOutlet weak var progressBar: UIProgressView!
    @IBOutlet weak var statusLabel: UILabel!
    
    // Replace with your actual download URL
    let targetDownloadURL = URL(string: "https://example.com/your-large-file.zip")!
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // Restore existing download state when the view loads
        if let progress = DownloadManager.shared.getCurrentProgress(for: targetDownloadURL) {
            updateUI(with: progress)
        } else {
            resetUI()
        }
        
        // Set up listeners for progress updates
        DownloadManager.shared.onProgressUpdate = { [weak self] url, progress in
            guard url == self?.targetDownloadURL else { return }
            self?.updateUI(with: progress)
        }
        
        // Set up listener for download completion/failure
        DownloadManager.shared.onDownloadComplete = { [weak self] url, location, error in
            guard url == self?.targetDownloadURL else { return }
            if let error = error {
                self?.statusLabel.text = "Failed: \(error.localizedDescription)"
            } else {
                self?.statusLabel.text = "Download Complete!"
                self?.progressBar.progress = 1.0
            }
        }
    }
    
    @IBAction func startDownloadTapped(_ sender: UIButton) {
        DownloadManager.shared.startDownload(for: targetDownloadURL)
        statusLabel.text = "Downloading..."
    }
    
    private func updateUI(with progress: Double) {
        progressBar.progress = Float(progress)
        statusLabel.text = String(format: "Downloading: %.1f%%", progress * 100)
    }
    
    private func resetUI() {
        progressBar.progress = 0.0
        statusLabel.text = "Ready to Download"
    }
}

Key Fixes Explained:

  • Singleton Persistence: The DownloadManager singleton lives for the app's entire lifecycle, so it retains all active download tasks even when your view controller is destroyed and recreated.
  • State Restoration: Every time the view appears, we check the singleton for existing progress and update the UI immediately.
  • Thread Safety: All UI updates are dispatched to the main thread to avoid crashes.
  • Weak Self: We use [weak self] in closures to prevent memory leaks between the singleton and view controller.

This setup ensures that background downloads keep running, and your UI always reflects the current state—even after navigating back and forth!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:54:02