【问题标题】:Add Progress bar to UIAlertController with showing update将进度条添加到 UIAlertController 并显示更新
【发布时间】:2017-01-27 16:25:55
【问题描述】:

我尝试在我的应用程序上添加进度条。我在How to add Progress bar to 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
    })
}

【问题讨论】:

  • 进度循环的运行速度有多快?您是否看到打印的进度缓慢增加,或者所有数字都在瞬间出现?在那里添加一点延迟,加载文件或其他内容,然后看看它的外观

标签: ios swift3 queue progress-bar uialertcontroller


【解决方案1】:

您的代码中的所有这些调度队列可能会让您有些困惑 :-)

我试图解释问题:

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

因此,

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

只有当您的警报视图通过viewDidLoadviewWillAppear 等方法显示时,您才需要first Dispatch,例如从尚未显示视图的时间点开始。如果您从按钮回调或viewDidAppear(例如视图可见)中调用它,您可以跳过外部调度。

给你 - 我还投入了一些睡眠时间并稍微修改了 GCD-Calls 以使用尾随闭包:

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);
            }
        }
    }
}

【讨论】:

  • 谢谢安德烈亚斯。您的代码就像魔术一样工作。我把我的代码放在下面作为有一些工作量的代码(不是睡眠)。
  • 几行简化的解决方案。
【解决方案2】:

在 Andreas 评论的帮助下。我用这段代码来解决问题。

 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: "loading", message: "wait please", preferredStyle: .alert)

        let rect = CGRect(x: 10, y: 70, width: 250, height: 0)
        let progressView = UIProgressView(frame: rect)
        progressView.progress = 0.0
        progressView.tintColor = UIColor.blue
        alert.view.addSubview(progressView)
        topViewController.present(alert, animated: true, completion: nil)
        var progress = 0.0
        DispatchQueue.global(qos: .background).async(execute: {
            repeat {
                progress += 0.0001
                print (progress)
                DispatchQueue.main.async(flags: .barrier, execute: {
                    progressView.progress = Float(progress)
                })
            } while progress < 1.0
            DispatchQueue.main.async(execute: {
                alert.dismiss(animated: true, completion: nil);
            })
        })

    })
}

【讨论】:

    猜你喜欢
    • 2015-10-19
    • 2018-04-21
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多