Reputation: 664
I need to parse quite a lot of text and wanted to provide some feedback to the user while this is taking place.
The environment is Swift and while I do see some code in Obj-C ([self performSelector:@selector(.....)), it doesn't make a lot of sense. If I knew how to take that and embed it into a Swift approach, I would do.
I can summarise the problem with a small reproducible case that gives the same result. i.e. within a loop, increment a value, show the progress and go back until done. Obviously, the progress isn't going to be shown because iOS waits until the loop is done before refreshing the screen.
That does make sense, so I would like at various intervals to interrupt the processing (i.e. the loop) and refresh the progress bar before continuing with the processing.
My current code looks like this:
@IBAction func goButton(sender: UIButton) {
currentCounter = 0
target = textTarget.text.toInt()!
step = textStep.text.toInt()!
updateProgressBar()
while currentCounter < target {
currentCounter += step
updateProgressBar()
}
}
func updateProgressBar() {
var percentage = Float(currentCounter) / Float(target) * 100
progressPercentage.text = "\(percentage) %"
println("progress = \(percentage)")
progressBar.setProgress(percentage, animated: false)
}
I've seen the following:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// do some task here...
}
dispatch_async(dispatch_get_main_queue()) {
// do another task here...
}
How might I use this approach (if it's relevant), and where would the processing versus the call to refresh come in if I did?
Jon
Upvotes: 2
Views: 6879
Reputation: 664
Here is an updated answer to this with code as I think others will benefit from the pattern:
@IBAction func goButton(sender: UIButton) {
target = textTarget.text.toInt()!
step = textStep.text.toInt()!
currentCounter = 0
updateProgressBar()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
self.doTimeConsumingWork()
}
}
func doTimeConsumingWork() {
while currentCounter < target {
currentCounter += step
percentage = Float(currentCounter) / Float(target) * 100
if (percentage > 0.0) && (percentage % 10.0) == 0.0 {
// This is the important bit :)
// I chose to do this synchronously (not async) since progress updating can actually be done quite a while after the time consuming work is over
dispatch_sync(dispatch_get_main_queue()) {
self.updateProgressBar()
}
}
}
println("Finished doing time consuming work")
}
func updateProgressBar() {
progressPercentage.text = "\(percentage) %"
println("target = \(target), step = \(step) progress = \(percentage)")
progressBar.setProgress(percentage / 100.0, animated: false)
}
Upvotes: 4
Reputation: 798
GCD (Grand Central Dispatch) is definitely the better way to go. It's simple, powerful, and intuitive to use, although the syntax might suggest otherwise at first.
The gist of the approach to update the UI when doing time-consuming work is always this:
dispatch_async
with a queue other than the main queue (there are built-in queues you can use for this or you can create your own)dispatch_async
, this time using the main queue (there's a way to access the main queue for that).dispatch_async
, you update your UI.For a nice and comprehensive tutorial on using GCD in the context of Swift, take a look at this.
Upvotes: 5