Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AlertController is not in the window hierarchy

I've just created a Single View Application project with ViewController class. I would like to show a UIAlertController from a function which is located inside my own class.

Here is my class with an alert.

class AlertController: UIViewController {
     func showAlert() { 
         var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
         self.presentViewController(alert, animated: true, completion: nil)
     }
}

Here is ViewController which executes the alert.

class ViewController: UIViewController {
   override func viewDidLoad() {
       super.viewDidLoad()  
   }

   @IBAction func showAlertButton(sender: AnyObject) {
       var alert = AlertController()
       alert.showAlert()
   }
}

This is what I get instead of a beautiful alert.

Warning: Attempt to present UIAlertController: 0x797d2d20 on Sprint1.AlertController: 0x797cc500 whose view is not in the window hierarchy!

What should I do?

like image 688
wtznc Avatar asked Mar 25 '15 13:03

wtznc


4 Answers

If you're instancing your UIAlertController from a modal controller, you need to do it in viewDidAppear, not in viewDidLoad or you'll get an error.

Here's my code (Swift 4):

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    let alertController = UIAlertController(title: "Foo", message: "Bar", preferredStyle: .alert)

    alertController.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
    present(alertController, animated: true, completion: nil)
}
like image 154
Skoua Avatar answered Oct 22 '22 19:10

Skoua


Let's look at your view hierarchy. You have a ViewController. Then you are creating an AlertController, you are not adding it to your hierarchy and you are calling an instance method on it, that attempts to use the AlertController as presenting controller to show just another controller (UIAlertController).

+ ViewController
    + AlertController (not in hierarchy)
        + UIAlertController (cannot be presented from AlertController)

To simplify your code

class ViewController: UIViewController {
   override func viewDidLoad() {
       super.viewDidLoad()  
   }

   @IBAction func showAlertButton(sender: AnyObject) {
       var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
       self.presentViewController(alert, animated: true, completion: nil)
   }
}

This will work.

If you need the AlertController for something, you will have to add it to the hierarchy first, e.g. using addChildViewController or using another presentViewController call.

If you want the class to be just a helper for creating alert, it should look like this:

class AlertHelper {
    func showAlert(fromController controller: UIViewController) { 
        var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
        controller.presentViewController(alert, animated: true, completion: nil)
    }
}

called as

 var alert = AlertHelper()
 alert.showAlert(fromController: self)
like image 20
Sulthan Avatar answered Oct 22 '22 19:10

Sulthan


You can use below function to call alert from any where just include these method in AnyClass

class func topMostController() -> UIViewController {
        var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
        while ((topController?.presentedViewController) != nil) {
            topController = topController?.presentedViewController
        }
        return topController!
    }

    class func alert(message:String){
        let alert=UIAlertController(title: "AppName", message: message, preferredStyle: .alert);
        let cancelAction: UIAlertAction = UIAlertAction(title: "OK", style: .cancel) { action -> Void in

        }
        alert.addAction(cancelAction)
        AnyClass.topMostController().present(alert, animated: true, completion: nil);
    }

Then call

AnyClass.alert(message:"Your Message")
like image 14
Varun Naharia Avatar answered Oct 22 '22 19:10

Varun Naharia


Write the following 3 lines, all we need to do is this.

Swift 3.0

private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
     UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
  }

Swift 2.0

  private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
     UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: flag, completion: completion)
  }
like image 5
Mitsuaki Ishimoto Avatar answered Oct 22 '22 17:10

Mitsuaki Ishimoto