1

So I have a function that shows an alert while Alamofire is doing some stuff and I want to dismiss the alert when Alamofire is done but sometimes it works and sometimes it doesn't! and when it doesn't work I'm getting the error (iOS 13, Xcode 11 Beta 5): Warning: Attempt to dismiss from view controller (UITabBarController: 0x7f90b7013a00) while a presentation or dismiss is in progress!

This is the function I use to show the alert:

func showLoadingDialog(show : Bool)  {
        let alert = UIAlertController(title: nil, message: "⏳ Please wait...", preferredStyle: .alert)
        if show == true {


            let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
            loadingIndicator.hidesWhenStopped = true
            loadingIndicator.style = .medium
            loadingIndicator.startAnimating()
            alert.view.addSubview(loadingIndicator)
            present(alert, animated: true, completion: nil)
        } else {
            dismiss(animated: true, completion: nil)
            // I've tried the "alert.dismiss" but it doesn't work!
            //alert.dismiss(animated: true, completion: nil)
            alert.removeFromParent()
        }
    }

Also I'm using tab bars and inside one of the tab bars I'm using the navigation controller and inside that I've a tableview so when the user clicks the cells it goes to another view controller (inside the same storyboard file) using this code:

let detailsVC = storyboard?.instantiateViewController(identifier: "Bdetails") as? DetailsController
    self.navigationController?.pushViewController(detailsVC!, animated: true)

and then I've also disabled the navigation bar (the header I guess and I'm using a custom button to return to the previews page using this code:

self.navigationController?.popViewController(animated: true)

also here is the Alamofire where I've put the alert function and I'm using it inside viewDidLoad:

func libHttp(url: String) {

    // Showing the alert.
    showLoadingDialog(show: true)
        Alamofire.request(url).responseJSON { (responseData) ->
            Void in
            if ((responseData.result.value) != nil) {

                let libJSON = JSON(responseData.result.value!)
                if let libData = libJSON.array {
                    for detail in libData {
                        // putting the values into an object from a custom class
                    }

                    // Updates the storyboard elements' value with the objects values.
                    self.update()

                    // dismissing the alert.
                    self.showLoadingDialog(show: false)
                }
            }
        }
    }
Taimoor Suleman
  • 1,588
  • 14
  • 29
arata
  • 859
  • 1
  • 8
  • 23
  • When you hide it you create a new alert, dismiss the viewController and remove the alert (that isn't even presented) from the viewController. You want to create the alert outside the `showLoadingDialog()` function – Vollan Aug 08 '19 at 09:41
  • Thx for the comment! so you're saying I've should create the alert inside the Alamofire function?! – arata Aug 08 '19 at 09:45
  • No, im saying you should create it on a class level instead of inside a function – Vollan Aug 08 '19 at 09:52
  • @Vollan tried that now, only works for one time and the second time it won't dismiss! (getting nothing in the console too) – arata Aug 08 '19 at 10:14

3 Answers3

0

This seems like a race condition involving when you call your libHttp(url:) function. Check for scenarios where you are dismissing the alert after you pop the viewController from the navigation controller.

Siva
  • 21
  • 5
0

Your showLoadingDialog(show : Bool) looks a little strange. When passing true to it, to display the alert, everything seems to be setup correctly, but when passing false, to hide the alert, you are creating a new UIAlertController, never actually present it, and then tries to remove it. I'm guessing that you really don't want to create a new UIAlertController here, but would instead like to remove an already presented UIAlertController?

Maybe you can do something like this instead?

func showLoadingDialog() -> UIAlertController  {
    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.style = .medium
    loadingIndicator.startAnimating()
    alert.view.addSubview(loadingIndicator)
    present(alert, animated: true, completion: nil)
    return alert
}

func libHttp(url: String) {

    // Showing the alert.
    let alert = showLoadingDialog()
    Alamofire.request(url).responseJSON { (responseData) ->
        Void in
        if ((responseData.result.value) != nil) {

            let libJSON = JSON(responseData.result.value!)
            if let libData = libJSON.array {
                for detail in libData {
                    // putting the values into an object from a custom class
                }

                // Updates the storyboard elements' value with the objects values.
                self.update()

                // dismissing the alert.
                alert.dismiss(animated: true, completion: nil)
            }
        }
    }
}
rodskagg
  • 3,827
  • 4
  • 27
  • 46
  • it works only for one time! when I click one of the table view cells after going to the next view controller it shows and works perfectly but after that I've clicked my custom button to go back to the previous view controller and then tapping again on one of the cells the alert won't dismiss! – arata Aug 08 '19 at 09:57
0

Your alert.dismiss call isn't working because you instanciate a new AlertController each time you call the showLoadingDialog.

Here is a minimum viable working example:

import UIKit

class ViewController: UIViewController {
    let alert = UIAlertController(title: nil, message: "⏳ Please wait...", preferredStyle: .alert)
    
    func showLoadingDialog(show : Bool) {
        if show {
            let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
            loadingIndicator.hidesWhenStopped = true
            loadingIndicator.style = .medium
            loadingIndicator.startAnimating()
            alert.view.addSubview(loadingIndicator)
            present(alert, animated: true, completion: nil)
        } else {
            alert.dismiss(animated: true, completion: nil)
        }
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        showLoadingDialog(show: true)

        DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
            self.showLoadingDialog(show: false)
        }
    }
}

Simulator Preview:

Simulator Preview

Community
  • 1
  • 1
YoshiJaeger
  • 1,180
  • 10
  • 14
  • it works only for one time! when I click one of the table view cells after going to the next view controller it shows and works perfectly but after that I've clicked my custom button to go back to the previous view controller and then tapping again on one of the cells the alert won't dismiss! (I tried to dismiss it when the Alamofire function is done) ( tested on both simulator and real device) – arata Aug 08 '19 at 10:01
  • Yeah sure, sounds like you are not calling one and the same method ```showLoadingDialog```. Make sure only one instance of ```ViewController```, respectively ```UIAlertController```, exists – YoshiJaeger Aug 09 '19 at 12:57