在我的代码中,我有一个简单的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)
}
}
答案 0 :(得分:37)
三个观察,两个基本,一个更先进:
除非循环本身在另一个线程上运行,否则您的循环将无法更新该主线程中的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)
}
}
}
在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
的{{1}}),发布DISPATCH_SOURCE_TYPE_DATA_ADD
次调用(Swift 2中的add
)后台线程尽可能频繁,并且UI将尽可能快地处理它们,但是当它调用dispatch_source_merge_data
(Swift 2中的data
)时,它们会将它们合并在一起,如果后台更新有更多内容比UI快,否则可以处理它们。这通过最佳UI更新实现了最大背景性能,但更重要的是,这确保了UI不会成为瓶颈。
因此,首先声明一些变量以跟踪进度:
dispatch_source_get_data
现在,您的循环可以创建源,定义更新源时要执行的操作,然后启动更新源的异步循环。在Swift 3中:
var progressCounter: UInt = 0
在Swift 2中:
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)
}
}