The problem you are facing is that the user interface changes, like the start of the activity indicator, it does not happen until your code returns, and the application maintains an event loop.
Your code is synchronous, so the activity indicator is never called.
You need to start the activity indicator, return, and then perform your laborious activity after the delay. You can use dispatch_after
for this (or DispatchQueue.main.asyncAfter
in Swift 3).
Here is an example IBAction
that launches an activity indicator within 2 seconds after clicking a button:
@IBAction func handleButton(_ sender: UIButton) { activityIndicator.startAnimating() sender.isEnabled = false DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
Edit:
Your code will look like this:
@IBAction func onCIFiltersButton(_ sender: UIButton) { if (sender.isSelected) { hideCIFilters() sender.isSelected = false } else { activityIndicator.startAnimating() DispatchQueue.main.asyncAfter(deadline: .now() + 0.0) { showCIFilters()
Edit # 2:
Again, user interface changes do not actually take place until your RETURNS code and applications use the event loop
If you have a code like this:
activityIndicator.startAnimating() sleep(2) //This is in place of your time-consuming synchronous code. activityIndicator.stopAnimating()
Then the following happens:
You queue a call to start the activity indicator the next time you return the code. (So ββthis is not the case.)
You are blocking the main thread with some kind of time-consuming task. As this continued, the activity indicator did not begin to rotate.
Finally, your laborious task is completed. Your code now calls activityIndicator.stopAnimating()
. Finally, your code returns, your application services event loop, as well as the calls you need to start, and then immediately stop the activity indicator call. As a result, the activity indicator does not actually rotate.
This code does it differently:
//Queue up a call to start the activity indicator activityIndicator.startAnimating() //Queue up the code in the braces to be called after a delay. //This call returns immediately, before the code in the braces is run. DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { showCIFilters() //the menu with UIView collection activityIndicator.stopAnimating() sender.isSelected = true }
Since the closure passed to the DispatchQueue.main.asyncAfter
call does not start immediately, this call returns immediately.
The application returns to service the event loop, and the activity indicator starts spinning. Shortly after this, the Grand Central Dispatch calls the block of code that you passed to it, which starts to perform your laborious operation (in the main thread), and then does cal to stop the activity indicator after the completion of the laborious task,