如何在 Swift 4 中压缩文件时显示进度 HUD?

How to show progress hud while compressing a file in Swift 4?

我正在使用marmelroy/Zip framework to zip/unzip files in my project, and JGProgressHUD来显示操作的进度。

如果我尝试从 ViewDidLoad 方法显示它,我可以看到 HUD,但是如果我在与 quickZipFiles 方法的进度功能关联的闭包中使用它(如代码示例), hud 仅在操作结束时显示。

我想这可能与计时问题有关,但由于我不太喜欢完成处理程序、闭包和 GDC(线程、异步任务等),所以我想征求一下建议。

有什么想法吗?

// In my class properties declaration
var hud = JGProgressHUD(style: .dark)

// In my ViewDidLoad
self.hud.indicatorView = JGProgressHUDPieIndicatorView()
self.hud.backgroundColor = UIColor(white: 0, alpha: 0.7)

// In my method
do {
    self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
    self.hud.detailTextLabel.text = "0%"
    if !(self.hud.isVisible) {
        self.hud.show(in: self.view)
    }
    zipURL = try Zip.quickZipFiles(documentsList, fileName: "documents", progress: { (progress) -> () in
        let progressMessage = "\(round(progress*100))%"
        print(progressMessage)
        self.hud.setProgress(Float(progress), animated: true)
        self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
        self.hud.detailTextLabel.text = progressMessage
        if (progress == 1.0) {
            self.hud.dismiss()
        }
    })
} catch {
    print("Error while creating zip...")
}

查看 zip 库的实现,所有 zipping/unzipping 和对进度处理程序的调用都在同一个线程上完成。主页上显示的示例不是很好,如果您想在压缩或解压缩时使用进度指示器更新 UI,则不能使用 as-is。

解决方法是在后台执行zipping/unzipping,在进度块中,更新主队列上的UI。

假设您正在从主队列调用您发布的代码(以响应用户执行某些操作),您应该按如下方式更新您的代码:

// In my class properties declaration
var hud = JGProgressHUD(style: .dark)

// In my ViewDidLoad
self.hud.indicatorView = JGProgressHUDPieIndicatorView()
self.hud.backgroundColor = UIColor(white: 0, alpha: 0.7)

self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
self.hud.detailTextLabel.text = "0%"
if !(self.hud.isVisible) {
    self.hud.show(in: self.view)
}

DispatchQueue.global().async {
    defer {
        DispatchQueue.main.async {
            self.hud.dismiss()
        }
    }

    do {
        zipURL = try Zip.quickZipFiles(documentsList, fileName: "documents", progress: { (progress) -> () in
            DispatchQueue.main.async {
                let progressMessage = "\(round(progress*100))%"
                print(progressMessage)
                self.hud.setProgress(Float(progress), animated: true)
                self.hud.textLabel.text = NSLocalizedString("Zipping files...", comment: "Zipping File Message")
                self.hud.detailTextLabel.text = progressMessage
            }
        })
    } catch {
        print("Error while creating zip...")
    }
}

ZIP Foundation built-in 支持进度报告和取消。
因此,如果您可以切换 ZIP 库,这可能更适合您的项目。 (完全公开:我是这个图书馆的作者)

下面是一些示例代码,展示了如何压缩目录并在 JGProgressHUD 上显示操作进度。我只是在这里压缩主包的目录作为示例。

ZIP 操作在单独的线程上分派,以便您的主线程可以更新 UI。 progress var 是一个默认的 Foundation (NS)Progress 对象,它通过 KVO 报告变化。

import UIKit
import ZIPFoundation
import JGProgressHUD

class ViewController: UIViewController {

    @IBOutlet weak var progressLabel: UILabel!
    var indicator = JGProgressHUD()
    var isObservingProgress = false
    var progressViewKVOContext = 0

    @objc
    var progress: Progress?

    func startObservingProgress()
    {
        guard !isObservingProgress else { return }

        progress = Progress()
        progress?.completedUnitCount = 0
        self.indicator.progress = 0.0

        self.addObserver(self, forKeyPath: #keyPath(progress.fractionCompleted), options: [.new], context: &progressViewKVOContext)
        isObservingProgress = true
    }

    func stopObservingProgress()
    {
        guard isObservingProgress else { return }

        self.removeObserver(self, forKeyPath: #keyPath(progress.fractionCompleted))
        isObservingProgress = false
        self.progress = nil
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == #keyPath(progress.fractionCompleted) {
            DispatchQueue.main.async {
                self.indicator.progress = Float(self.progress?.fractionCompleted ?? 0.0)
                if let progressDescription = self.progress?.localizedDescription {
                    self.progressLabel.text = progressDescription
                }

                if self.progress?.isFinished == true {
                    self.progressLabel.text = ""
                    self.indicator.progress = 0.0
                }
            }
        } else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }

    @IBAction func cancel(_ sender: Any) {
        self.progress?.cancel()
    }

    @IBAction func createFullArchive(_ sender: Any) {
        let directoryURL = Bundle.main.bundleURL

        let tempArchiveURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString).appendingPathExtension("zip")
        self.startObservingProgress()
        DispatchQueue.global().async {
            try? FileManager.default.zipItem(at: directoryURL, to: tempArchiveURL, progress: self.progress)
            self.stopObservingProgress()
        }
    }
}