I created a simple, one screen iOS app with only two controls, an activity indicator and a button, as shown below. When I run the code, I get the "starting" message, and 5 seconds later, the "stopping" message, but the activity indicator just displays without spinning. I must be missing something basic. This has had me stumped for weeks.
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
@IBAction func startButton(_ sender: Any) {
activityIndicator.startAnimating()
print("starting")
sleep(5)
print("stopping")
activityIndicator.stopAnimating()
}
}
2
Answers
In my code, I'm now using the DispatchQueue instead of calling "rescoreAllDecks" directly:
I also created a separate function to stop the spinner:
This does solves the problem. I am going to take a note of this solution for future reference. Thanks again.
You are blocking the main thread and preventing any updates or interactions for 5 seconds. Screen does not refresh and in production your app would have been terminated by watchdog.
Try something like this:
To explain on it there are multiple ways to skin this cat. But in general never block the main thread with anything. Either you have expensive operation or you are waiting for something you should do that on another thread.
Since you are just passing time there is no reason to use another thread. You can use a timer or dispatch queue. Timer would look like this:
But if you wanted to use another thread it would mean switching to another thread, waiting for 5 seconds (you could use sleep) and then switching back to main thread to stop the animation. So quite unideal to do.
I hope this gives you some more understanding on the topic.
EDIT: From comment to replace the wait time with using CoreData to do some long lasting operations.
Core data has contexts. Each context represents an in-memory state of your database which is lazily loaded.
To do heavy load you want to create a new background context, have it perform all of your logic, save to database and report back that it is done.
This can be used with your logic like so:
It is important to see that intentionally a context already has a
perform
method which will perform logic on the thread that context was designed to be on. It may be main thread or it might be some other thread. For that reason it is important to useDispatchQueue.main.async
to come back to main thread so that UI API can be called.Also note that placing
sleep
inside thisperform
operation actually would wait for given time and then activity indicator would have been working correctly since the context is not performing the sleep on the main thread.