acoustickat
acoustickat

Reputation: 39

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

}

Upvotes: 1

Views: 4687

Answers (4)

Nitesh
Nitesh

Reputation: 2024

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

Upvotes: 2

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

Upvotes: 2

Wide Angle Technology
Wide Angle Technology

Reputation: 1222

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
    }

Upvotes: 0

Bishan
Bishan

Reputation: 401

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.

Upvotes: -1

Related Questions