AlertController is not in the window hierarchy

陌路散爱 提交于 2019-11-27 03:50:00

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

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)
Varun Naharia

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")
Mitsuaki Ishimoto

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

If you want to create a separate class for displaying alert like this, subclass NSObject not UIViewController.

And pass the ViewControllers reference from which it is initiated, to the showAlert function so that you can present alert view there.

Here is the code of an UIAlertController in a Utility.swift class (not a UIViewController) in Swift3, Thanks Mitsuaki!

private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
    UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
func warningAlert(title: String, message: String ){
    let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
    alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler:  { (action) -> Void in
    }))        
 //   self.present(alert, animated: true, completion: nil)
    presentViewController(alert: alert, animated: true, completion: nil)
}

It helped me to stick a slight delay between the viewDidLoad method and firing the alert method:

   [self performSelector:@selector(checkPhotoPermission) withObject:nil afterDelay:0.1f];

This worked for me:

- (UIViewController *)topViewController{
  return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController *)topViewController:(UIViewController *)rootViewController
{
  if (rootViewController.presentedViewController == nil) {
    return rootViewController;
  }

  if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
    UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
    UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
    return [self topViewController:lastViewController];
  }

  UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
  return [self topViewController:presentedViewController];
}

Implementation:

UIViewController * topViewController = [self topViewController];

Using with alert:

[topViewController presentViewController:yourAlert animated:YES completion:nil];

You can send an alert from any class in your app (that uses UIKit: #import <UIKit/UIKit.h> )

Source here.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!