在Swift中使用Dispatch_Async更新UI

dsl*_*fjl 9 swift

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

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)

    }
  }
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 39

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

  1. 除非循环本身在另一个线程上运行,否则您的循环将无法更新该主线程中的UI.因此,您可以将其分派到某个后台队列.在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)
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    在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)
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 另请注意,进度是一个从0.0到1.0的数字,因此您可能希望除以循环的最大迭代次数.

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

    可以使用一个DispatchSourceUserDataAdd(在斯威夫特2,这是一个dispatch_source_tDISPATCH_SOURCE_TYPE_DATA_ADD),后add调用(dispatch_source_merge_data斯威夫特2)从后台线程根据需要频繁的,而用户界面将尽可能快,因为它可以处理它们,但是当它调用聚结在一起data(dispatch_source_get_data在Swift 2中)如果后台更新比UI更快地处理它们.这通过最佳UI更新实现了最大背景性能,但更重要的是,这确保了UI不会成为瓶颈.

    因此,首先声明一些变量以跟踪进度:

    var progressCounter: UInt = 0
    
    Run Code Online (Sandbox Code Playgroud)

    现在,您的循环可以创建源,定义更新源时要执行的操作,然后启动更新源的异步循环.在Swift 3中,它是:

    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)
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    在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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)