I'm using a loading modal pretty much according to this topic:
Loading an "overlay" when running long tasks in iOS
I use the same code in several ViewControllers, so I created an extension:
extension UIViewController {
func showLoading() {
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: false, completion: nil)
}
func hideLoading() {
if ( presentedViewController != nil && !presentedViewController!.isBeingPresented ) {
dismiss(animated: false, completion: nil)
}
}
}
I typically use the code like this:
self.showLoading()
callNetwork() { response in
DispatchQueue.main.async {
self.hideLoading()
....
}
}
If the network call takes 0.5s or more, everything works fine. The issue is if the network is too fast. Then I'll get an error similar to this one:
Warning: Attempt to dismiss from view controller <UINavigationController: 0x7ff581830a00> while a presentation or dismiss is in progress!
And the modal won't get dismissed.
The best solution I can come up with is something like this (super class instead of extension as extensions can't have variables):
class LoadingViewController: UIViewController {
var shouldDismissImmediately = false
func showLoading() {
shouldDismissImmediately = false
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: false) {
if (self.shouldDismissImmediately) {
self.dismiss(animated: false, completion: nil)
}
}
}
func hideLoading() {
if ( presentedViewController != nil && !presentedViewController!.isBeingPresented ) {
dismiss(animated: false, completion: nil)
} else {
shouldDismissImmediately = true
}
}
}
Can anyone think of a better solution? This one just doesn't feel right. Maybe I'm doing something fundamentally wrong. Like - should I even present such a dialog when waiting for a network response? Is there a better way of making the user to wait? I need the user to be aware that something is happening and in the same time, I need him not to be able to press any buttons in the UI.