16

I have trouble to display my UIAlertController because I'm trying to show it in a Class which is not an ViewController.

I already tried adding it:

 var alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .Alert)

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)

Which is not working... I didn't find any solution that worked for me yet.

Michael
  • 1,030
  • 14
  • 29
  • You're class which is not a UIViewController now is partly untestable. Consider adding a delegate or a block based callback to display the alert on the view controller that's using this class. – 3lvis May 25 '16 at 23:48

4 Answers4

38

I wrote this extension over UIAlertController to bring back show().
It uses recursion to find the current top view controller:

extension UIAlertController {
    
    func show() {
        present(animated: true, completion: nil)
    }
    
    func present(animated: Bool, completion: (() -> Void)?) {
        if let rootVC = UIApplication.shared.keyWindow?.rootViewController {
            presentFromController(controller: rootVC, animated: animated, completion: completion)
        }
    }
    
    private func presentFromController(controller: UIViewController, animated: Bool, completion: (() -> Void)?) {
        if 
            let navVC = controller as? UINavigationController,
            let visibleVC = navVC.visibleViewController
        {
            presentFromController(controller: visibleVC, animated: animated, completion: completion)
        } else if
            let tabVC = controller as? UITabBarController,
            let selectedVC = tabVC.selectedViewController
        {
            presentFromController(controller: selectedVC, animated: animated, completion: completion)
        } else if let presented = controller.presentedViewController {
            presentFromController(controller: presented, animated: animated, completion: completion)
        } else {
            controller.present(self, animated: animated, completion: completion);    
        }
    }
}

Now it's as easy as:

var alertController = UIAlertController(title: "Title", message: "Message", preferredStyle: .Alert)
alertController.show()
Aviel Gross
  • 9,770
  • 3
  • 52
  • 62
  • 1
    Glad it works for you, I have been using it with many VC scenarios (modal vc on a nav stack inside a tab bar controller etc.), please let me know if theres a situation where this doesn't work... – Aviel Gross May 05 '15 at 13:45
  • SharedApplication cannot be used in an App Extension. – SAHM Nov 18 '16 at 17:31
  • 2
    This doesn't work in the case where the key view controller is already presenting something modally. I had to add another check `if let presented = controller.presentedViewController { presentFromController(controller: presented, animated: animated, completion: completion) } else { controller.present(self, animated: animated, completion: completion) }` – Tim Oct 09 '17 at 23:48
3

This should work.

 UIApplication.sharedApplication().windows[0].rootViewController?.presentViewController(...)
Steve Glick
  • 698
  • 7
  • 19
1

Create a helper function that you call from the current view controller and pass the current view controller as a parameter:

func showAlertInVC(
  viewController: UIViewController, 
  title: String, 
message: String)
{
  //Code to create an alert controller and display it in viewController
}
Duncan C
  • 128,072
  • 22
  • 173
  • 272
0

If you solution is not working it probably because of there is no window at that moment. I had the same problem when I was trying to show alert view in application:DidFinishLoadingWithOptions method. In this case my solution was to check if root view controller is available, and if it's not, then add notification for UIApplicationDidBecomeActiveNotification

NSNotificationCenter.defaultCenter().addObserverForName(UIApplicationDidBecomeActiveNotification,
                object: nil,
                queue: NSOperationQueue.mainQueue()) {
                    (_) in
                        //show your alert by using root view controller
                        //remove self from observing
                    }
        }
Vasyl Khmil
  • 2,548
  • 1
  • 20
  • 36