在 Swift 中使用 Dispatch_Async 更新 UI [英] Updating the UI Using Dispatch_Async in Swift

查看:28
本文介绍了在 Swift 中使用 Dispatch_Async 更新 UI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的代码中,我有一个简单的 for 循环,它使用嵌套的 for 循环循环 100 次以创建延迟.延迟后,我通过 dispatch_async 更新 UI 中的进度视图元素.但是,我无法更新 UI.有谁知道为什么UI没有更新?注意:下面的打印语句用于验证 for 循环是否正确循环.

In my code I have a simple for loop that loops 100 times with nested for loops to create a delay. After the delay, I am updating a progress view element in the UI through a dispatch_async. However, I cannot get the UI to update. Does anyone know why the UI is not updating? Note: The print statement below is used to verify that the for loop is looping correctly.

for i in 0..<100 {

    //Used to create a delay
    for var x = 0; x<100000; x++ {
        for var z = 0; z<1000; z++ {

        }
    }

    println(i)

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
        self.progressView.setProgress(Float(i), animated: true)

    }
  }

推荐答案

三个观察,两个基本,一个更高级:

Three observations, two basic, one a little more advanced:

  1. 除非循环本身正在另一个线程上运行,否则您的循环将无法更新该主线程中的 UI.因此,您可以将其分派到某个后台队列.在 Swift 3 中:

  1. Your loop will not be able to update the UI in that main thread unless the loop itself is running on another thread. So, you can dispatch it to some background queue. In Swift 3:

DispatchQueue.global(qos: .utility).async {
    for i in 0 ..< kNumberOfIterations {

        // do something time consuming here

        DispatchQueue.main.async {
            // now update UI on main thread
            self.progressView.setProgress(Float(i) / Float(kNumberOfIterations), animated: true)
        }
    }
}

在 Swift 2 中:

In Swift 2:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    for i in 0 ..< kNumberOfIterations {

        // do something time consuming here

        dispatch_async(dispatch_get_main_queue()) {
            // now update UI on main thread
            self.progressView.setProgress(Float(i) / Float(kNumberOfIterations), animated: true)
        }
    }
}

  • 另请注意,进度是一个从 0.0 到 1.0 的数字,因此您可能希望除以循环的最大迭代次数.

  • Also note that the progress is a number from 0.0 to 1.0, so you presumably want to divide by the maximum number of iterations for the loop.

    如果 UI 更新来自后台线程的速度比 UI 处理它们的速度快,主线程可能会积压更新请求(使其看起来比实际慢得多).为了解决这个问题,可以考虑使用调度源将更新 UI"任务与实际的后台更新过程分离.

    If UI updates come more quickly from the background thread than the UI can handle them, the main thread can get backlogged with update requests (making it look much slower than it really is). To address this, one might consider using dispatch source to decouple the "update UI" task from the actual background updating process.

    可以使用 DispatchSourceUserDataAdd(在 Swift 2 中,它是 DISPATCH_SOURCE_TYPE_DATA_ADDdispatch_source_t),发布 add根据需要从后台线程调用(Swift 2 中的dispatch_source_merge_data),UI 将尽快处理它们,但在调用 data(Swift 2 中的 dispatch_source_get_data)如果后台更新的速度比 UI 处理它们的速度更快.这样可以通过优化 UI 更新实现最大的后台性能,但更重要的是,这可以确保 UI 不会成为瓶颈.

    One can use a DispatchSourceUserDataAdd (in Swift 2, it's a dispatch_source_t of DISPATCH_SOURCE_TYPE_DATA_ADD), post add calls (dispatch_source_merge_data in Swift 2) from the background thread as frequently as desired, and the UI will process them as quickly as it can, but will coalesce them together when it calls data (dispatch_source_get_data in Swift 2) if the background updates come in more quickly than the UI can otherwise process them. This achieves maximum background performance with optimal UI updates, but more importantly, this ensures the UI won't become a bottleneck.

    所以,首先声明一些变量来跟踪进度:

    So, first declare some variable to keep track of the progress:

    var progressCounter: UInt = 0
    

    现在您的循环可以创建一个源,定义更新源时要做什么,然后启动更新源的异步循环.在 Swift 3 中:

    And now your loop can create a source, define what to do when the source is updated, and then launch the asynchronous loop which updates the source. In Swift 3 that is:

    progressCounter = 0
    
    // create dispatch source that will handle events on main queue
    
    let source = DispatchSource.makeUserDataAddSource(queue: .main)
    
    // tell it what to do when source events take place
    
    source.setEventHandler() { [unowned self] in
        self.progressCounter += source.data
    
        self.progressView.setProgress(Float(self.progressCounter) / Float(kNumberOfIterations), animated: true)
    }
    
    // start the source
    
    source.resume()
    
    // now start loop in the background
    
    DispatchQueue.global(qos: .utility).async {
        for i in 0 ..< kNumberOfIterations {
            // do something time consuming here
    
            // now update the dispatch source
    
            source.add(data: 1)
        }
    }
    

    在 Swift 2 中:

    In Swift 2:

    progressCounter = 0
    
    // create dispatch source that will handle events on main queue
    
    let source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
    
    // tell it what to do when source events take place
    
    dispatch_source_set_event_handler(source) { [unowned self] in
        self.progressCounter += dispatch_source_get_data(source)
    
        self.progressView.setProgress(Float(self.progressCounter) / Float(kNumberOfIterations), animated: true)
    }
    
    // start the source
    
    dispatch_resume(source)
    
    // now start loop in the background
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        for i in 0 ..< kNumberOfIterations {
    
            // do something time consuming here
    
            // now update the dispatch source
    
            dispatch_source_merge_data(source, 1);
        }
    }
    

  • 这篇关于在 Swift 中使用 Dispatch_Async 更新 UI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆