1
votes

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()

}
4
You're probably calling createAlert in viewDidLoad or somewhere else. Could you please add your whole view controller code?Pranav Kasetti
@PranavKasetti I have updated my post with the whole view controller codeacoustickat
ok. what view controller are you navigating to with the segue?Pranav Kasetti
@PranavKasetti to a tab bar controller viewacoustickat
Why self.dismiss(animated: true, completion: nil) in the completion handler of your UIAlertAction? The alert is automatically dismissed if you declare the completion handler as nil and self refers to the view controller the alert is presented from in this context, so you are not dismissing the alert like that.Dávid Pásztor

4 Answers

2
votes

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)
        }
}
2
votes

Whenever We are trying to present UIAlertController inside any closure, We should call it on the main thread like:

DispatchQueue.main.async { [weak self] in
    self?.createAlert(title: "Sign in error", message: displayErrorMessage)
}
0
votes

Try this code for Swift 3

func displayMyAlertMessageError(userMessage:String, titleHead: String)
    //define displyMyAlertMessage.
{
    let MyAlert = UIAlertController(title: titleHead, message: userMessage, preferredStyle:UIAlertControllerStyle.alert);
    let okAction = UIAlertAction(title: "Okay", style: UIAlertActionStyle.default, handler: nil);MyAlert.addAction(okAction);
    self.present(MyAlert,animated:true,completion:nil);
}

@IBAction func signInPressed(_ sender: Any) {

  if (usernameTextField.text?.isEmpty) || (passwordTextField.text?.isEmpty)
    {
      createAlert(title: "Error in form", message: "Please enter an email and password.")
    }
  else 
    {
      //Your code here
    }
-1
votes

Change your createAlert method to this,

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

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

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

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

        }))
        controller.present(alert, animated: true, completion: nil)

    }

And then create alerts like this,

self.createAlert(title: "Sign in error", message: "Please try again later.", controller: self)

This might solve your problem.