Search code examples
iosswiftuialertcontroller

Attempt to present UIAlertController whose view is not in the window hierarchy (Swift 3/Xcode 8)


I'm trying to create an app and I want to show an alert when there is a login error or if the user forget to enter a username and/or password. However, I always get this warning:

Warning: Attempt to present on whose view is not in the window hierarchy!

I have tried the other solutions I found here but I still can't fix it. Here's my code:

func createAlert(title: String, message: String) {

    let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)

    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in

        self.dismiss(animated: true, completion: nil)

    }))

    self.present(alert, animated: true, completion: nil)

}

@IBAction func signInPressed(_ sender: Any) {

    if usernameTextField.text == "" || passwordTextField.text == "" {

        createAlert(title: "Error in form", message: "Please enter an email and password.")

    } else {

        var activityIndicator = UIActivityIndicatorView()

        activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        activityIndicator.center = self.view.center
        activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
        view.addSubview(activityIndicator)
        activityIndicator.startAnimating()
        UIApplication.shared.beginIgnoringInteractionEvents()

        PFUser.logInWithUsername(inBackground: usernameTextField.text!, password: passwordTextField.text!, block: { (user, error) in

            activityIndicator.stopAnimating()
            UIApplication.shared.endIgnoringInteractionEvents()

            if error != nil {

                var displayErrorMessage = "Please try again later."

                let error = error as NSError?

                if let errorMessage = error?.userInfo["error"] as? String {

                    displayErrorMessage = errorMessage

                }

                self.createAlert(title: "Sign in error", message: displayErrorMessage)


            } else {

                print("Logged in")

                self.performSegue(withIdentifier: "toSignIn", sender: self)

            }


        })

    }

}

UPDATE: Here's the whole view controller

class ViewController: UIViewController {

@IBOutlet var usernameTextField: UITextField!
@IBOutlet var passwordTextField: UITextField!

func createAlert(title: String, message: String) {

    let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)

    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in

        self.dismiss(animated: true, completion: nil)

    }))

    self.present(alert, animated: true, completion: nil)

}

@IBAction func signInPressed(_ sender: Any) {

    if usernameTextField.text == "" || passwordTextField.text == "" {

        createAlert(title: "Error in form", message: "Please enter an email and password.")

    } else {

        var activityIndicator = UIActivityIndicatorView()

        activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        activityIndicator.center = self.view.center
        activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
        view.addSubview(activityIndicator)
        activityIndicator.startAnimating()
        UIApplication.shared.beginIgnoringInteractionEvents()

        PFUser.logInWithUsername(inBackground: usernameTextField.text!, password: passwordTextField.text!, block: { (user, error) in

            activityIndicator.stopAnimating()
            UIApplication.shared.endIgnoringInteractionEvents()

            if error != nil {

                var displayErrorMessage = "Please try again later."

                let error = error as NSError?

                if let errorMessage = error?.userInfo["error"] as? String {

                    displayErrorMessage = errorMessage

                }

                self.createAlert(title: "Sign in error", message: displayErrorMessage)


            } else {

                print("Logged in")

                self.performSegue(withIdentifier: "toSignIn", sender: self)

            }


        })

    }

}

override func viewDidAppear(_ animated: Bool) {


    if PFUser.current() != nil {

        performSegue(withIdentifier: "toSignIn", sender: self)

    }

    self.tabBarController?.tabBar.isHidden = true

}

override func viewDidLoad() {
    super.viewDidLoad()

}

Solution

  • First create UIAlertController such an attribute.

    var alertController: UIAlertController?
    

    And you must add this in the viewDidLoad() like this:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        self.alertController = UIAlertController(title: "Alert", message: "Not images yet", preferredStyle: .alert)
        self.alertController?.addAction(UIAlertAction(title: "Close", style: .default))
        view.addSubview((alertController?.view)!)
    
    }
    

    So when you press signInButton and login is incorrect you must invoke.

    @IBAction func signInPressed(_ sender: Any) {
    
    if usernameTextField.text == "" || passwordTextField.text == "" {
    
        createAlert(title: "Error in form", message: "Please enter an email and password.")
    
    } else {
    
        var activityIndicator = UIActivityIndicatorView()
    
        activityIndicator = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
        activityIndicator.center = self.view.center
        activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
        view.addSubview(activityIndicator)
        activityIndicator.startAnimating()
        UIApplication.shared.beginIgnoringInteractionEvents()
    
        PFUser.logInWithUsername(inBackground: usernameTextField.text!, password: passwordTextField.text!, block: { (user, error) in
    
            activityIndicator.stopAnimating()
            UIApplication.shared.endIgnoringInteractionEvents()
    
            if error != nil {
    
                self.presentedViewController?.present(self.alertController!, animated: true, completion: nil)
            }
    }