如何在 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()
}
}
}
我正在使用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()
}
}
}