在我的代码中,我有一个简单的for循环,使用嵌套的for循环循环100次以创建延迟。延迟之后,我将通过dispatch_async更新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) } }
三种观察,两种基本观察,一种更为先进:
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中:
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的数字,因此您大概想除以循环的最大迭代次数。
如果UI更新来自后台线程的速度快于UI无法处理的速度,则主线程可能会被更新请求积压(这使它看起来比实际速度慢得多)。为了解决这个问题,可以考虑使用调度源将“更新UI”任务与实际的后台更新过程分离。
可以使用DispatchSourceUserDataAdd(在Swift 2中dispatch_source_t为DISPATCH_SOURCE_TYPE_DATA_ADD),在后台线程中根据需要频繁地发布add调用(dispatch_source_merge_data在Swift 2中为),UI会尽快处理它们,但在调用时将它们合并在一起data(dispatch_source_get_data在Swift 2中),如果后台更新的传入速度比UI可以处理它们的速度更快。这样可以通过最佳的UI更新获得最大的后台性能,但是更重要的是,这可以确保UI不会成为瓶颈。
DispatchSourceUserDataAdd
dispatch_source_t
DISPATCH_SOURCE_TYPE_DATA_ADD
add
dispatch_source_merge_data
data
dispatch_source_get_data
因此,首先声明一些变量以跟踪进度:
var progressCounter: UInt = 0
现在,您的循环可以创建一个源,定义源更新时的操作,然后启动异步循环来更新源。在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) } }
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); } }