将进度条添加到UIAlertController并显示更新

0 queue ios progress-bar uialertcontroller swift3

我尝试在我的应用程序上添加进度条.我找到了关于如何向UIAlertController添加进度条的问题?但它没有显示如何更新进度条.我简化了下面的代码,但没有更新进度条(只有在完成进度后才会显示).我看了什么?谢谢您的帮助.

override func viewDidLoad() {
    super.viewDidLoad()
    var topViewController = UIApplication.shared.delegate!.window!!.rootViewController!
    while (topViewController.presentedViewController != nil){
        topViewController = topViewController.presentedViewController!
    }
    DispatchQueue.main.async(execute: {
        let alert = UIAlertController(title: "downloading", message: "pls wait", preferredStyle: .alert)
        let progressBar = UIProgressView(progressViewStyle: .default)
        progressBar.setProgress(0.0, animated: true)
        progressBar.frame = CGRect(x: 10, y: 70, width: 250, height: 0)
        alert.view.addSubview(progressBar)
        topViewController.present(alert, animated: true, completion: nil)
        var progress: Float = 0.0
        repeat {
            DispatchQueue.global(qos: .background).async(execute: {
                progress += 0.01
                print (progress)
                DispatchQueue.main.async(flags: .barrier, execute: {
                    progressBar.setProgress(progress, animated: true)
                })
            })
        } while progress < 1.0
    })
}
Run Code Online (Sandbox Code Playgroud)

And*_*jen 5

人们可能会对代码中的所有调度队列感到困惑:-)

我试着解释一下这个问题:

一个(最 DispatchQueue.main.async外面的)在主(UI)线程中被执行.它创建UIAlertController,填充UIProgressView它并显示对话框.然后它在主线程中执行一些时间关键的工作(循环重复).永远不要这样做,因为它阻止了主线程.由于主线程被阻止,因此UI控件中不会反映任何更新,因此您不会看到进度更改.

因此,

  • 我将时间关键工作项(重复循环)作为异步工作项放入全局队列(而不是主队列)
    • 在该工作项中,我将进度条更新分派到主队列中(在主线程中执行)
    • 最后,当一切都完成后,我解雇了 UIAlertController

当您从类似方法或从例如尚未显示视图的时间点显示警报视图时,您只需要第一个 Dispatch .如果您从按钮回调中调用它,或者(例如视图可见),您可以跳过该外部调度.viewDidLoadviewWillAppearviewDidAppear

在这里 - 我也放了一些睡眠时间并修改了GCD-Calls litte以使用尾随闭包:

override func viewDidLoad() {
    super.viewDidLoad()
    var topViewController = UIApplication.shared.delegate!.window!!.rootViewController!
    while (topViewController.presentedViewController != nil){
        topViewController = topViewController.presentedViewController!
    }

    // Usage of GCD here is only necessary because it's called from
    // viewDidLoad. If called by viewDidAppear or some action callback, you 
    // don't need it:
    DispatchQueue.main.async {
        let alert = UIAlertController(title: "downloading", message: "pls wait", preferredStyle: .alert)
        let progressBar = UIProgressView(progressViewStyle: .default)
        progressBar.setProgress(0.0, animated: true)
        progressBar.frame = CGRect(x: 10, y: 70, width: 250, height: 0)
        alert.view.addSubview(progressBar)
        topViewController.present(alert, animated: true, completion: nil)
        var progress: Float = 0.0
        // Do the time critical stuff asynchronously
        DispatchQueue.global(qos: .background).async {
            repeat {
                progress += 0.1
                Thread.sleep(forTimeInterval: 0.25)
                print (progress)
                DispatchQueue.main.async(flags: .barrier) {
                    progressBar.setProgress(progress, animated: true)
                }
            } while progress < 1.0
            DispatchQueue.main.async {
                alert.dismiss(animated: true, completion: nil);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)