Comment présenter UIAlertController lorsqu'il n'est pas dans un controller de vue?

Scénario: L'user appuie sur un button sur un controller de vue. Le controller de vue est le plus haut (évidemment) dans la stack de navigation. Le tap appelle une méthode de class utilitaire appelée dans une autre class. Une mauvaise chose se produit là-bas et je veux afficher une alerte juste avant que le contrôle returnne au controller de vue.

+ (void)myUtilityMethod { // do stuff // something bad happened, display an alert. } 

C'était possible avec UIAlertView (mais peut-être pas tout à fait correct).

Dans ce cas, comment présentez-vous un UIAlertController , dans myUtilityMethod ?

A la WWDC, je me suis arrêté dans l'un des laboratoires et j'ai demandé à un ingénieur Apple la même question: "Quelle était la meilleure pratique pour afficher un UIAlertController ?" Et il a dit qu'ils avaient beaucoup posé cette question et nous avons plaisanté en disant qu'ils auraient dû avoir une séance là-dessus. Il a déclaré UIWindow créait en interne une UIWindow avec un UIViewController transparent et présentait ensuite le UIAlertController . Fondamentalement, qu'est-ce qui est dans la réponse de Dylan Betterman?

Mais je ne voulais pas utiliser une sous-class de UIAlertController car cela nécessiterait que je change mon code dans mon application. Donc, avec l'aide d'un object associé, j'ai créé une catégorie sur UIAlertController qui fournit une méthode show en Objective-C.

Voici le code pertinent:

 #import "UIAlertController+Window.h" #import <objc/runtime.h> @interface UIAlertController (Window) - (void)show; - (void)show:(BOOL)animated; @end @interface UIAlertController (Private) @property (nonatomic, strong) UIWindow *alertWindow; @end @implementation UIAlertController (Private) @dynamic alertWindow; - (void)setAlertWindow:(UIWindow *)alertWindow { objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (UIWindow *)alertWindow { return objc_getAssociatedObject(self, @selector(alertWindow)); } @end @implementation UIAlertController (Window) - (void)show { [self show:YES]; } - (void)show:(BOOL)animated { self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.alertWindow.rootViewController = [[UIViewController alloc] init]; id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate; // Applications that does not load with UIMainStoryboardFile might not have a window property: if ([delegate respondsToSelector:@selector(window)]) { // we inherit the main window's tintColor self.alertWindow.tintColor = delegate.window.tintColor; } // window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard) UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject; self.alertWindow.windowLevel = topWindow.windowLevel + 1; [self.alertWindow makeKeyAndVisible]; [self.alertWindow.rootViewController presentViewController:self animated:animated completion:nil]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; // precaution to insure window gets destroyed self.alertWindow.hidden = YES; self.alertWindow = nil; } @end 

Voici un exemple d'utilisation:

 // need local variable for TextField to prevent retain cycle of Alert otherwise UIWindow // would not disappear after the Alert was dismissed __block UITextField *localTextField; UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Global Alert" message:@"Enter some text" preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { NSLog(@"do something with text:%@", localTextField.text); // do NOT use alert.textfields or otherwise reference the alert in the block. Will cause retain cycle }]]; [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) { localTextField = textField; }]; [alert show]; 

L' UIWindow créée sera détruite lorsque le UIAlertController est désalloué, car c'est le seul object qui conserve l' UIWindow . Mais si vous affectez le UIAlertController à une propriété ou que son nombre de rétention augmente en accédant à l'alerte dans l'un des blocs d'action, l' UIWindow rest à l'écran et verrouille votre interface user. Voir l'exemple de code d'utilisation ci-dessus pour éviter de devoir accéder à UITextField .

J'ai fait un repo GitHub avec un projet de test: FFGlobalAlertController

Vous pouvez faire ce qui suit avec Swift 2.2:

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

Et Swift 3.0:

 let alertController: UIAlertController = ... UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil) 

Objectif c

 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"message" preferredStyle:UIAlertControllerStyleAlert]; //... id rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController; if([rootViewController isKindOfClass:[UINavigationController class]]) { rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject; } if([rootViewController isKindOfClass:[UITabBarController class]]) { rootViewController = ((UITabBarController *)rootViewController).selectedViewController; } [rootViewController presentViewController:alertController animated:YES completion:nil]; 

Swift 2.3

 let alertController = UIAlertController(title: "title", message: "message", preferredStyle: .Alert) //... var rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController if let navigationController = rootViewController as? UINavigationController { rootViewController = navigationController.viewControllers.first } if let tabBarController = rootViewController as? UITabBarController { rootViewController = tabBarController.selectedViewController } rootViewController?.presentViewController(alertController, animated: true, completion: nil) 

Swift 3

 let alertController = UIAlertController(title: "title", message: "message", preferredStyle: .alert) //... var rootViewController = UIApplication.shared.keyWindow?.rootViewController if let navigationController = rootViewController as? UINavigationController { rootViewController = navigationController.viewControllers.first } if let tabBarController = rootViewController as? UITabBarController { rootViewController = tabBarController.selectedViewController } rootViewController?.present(alertController, animated: true, completion: nil) 

UIAlertController générique UIAlertController pour tous les cas de UINavigationController et / ou UITabBarController . Fonctionne également s'il y a un VC modal sur l'écran pour le moment.

Usage:

 //option 1: myAlertController.show() //option 2: myAlertController.present(animated: true) { //completion code... } 

C'est l'extension:

 //Uses Swift1.2 syntax with the new if-let // so it won't comstack on a lower version. extension UIAlertController { func show() { present(animated: true, completion: nil) } func present(#animated: Bool, completion: (() -> Void)?) { if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController { presentFromController(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(visibleVC, animated: animated, completion: completion) } else { if let tabVC = controller as? UITabBarController, let selectedVC = tabVC.selectedViewController { presentFromController(selectedVC, animated: animated, completion: completion) } else { controller.presentViewController(self, animated: animated, completion: completion) } } } } 

J'ai posté une question similaire il y a quelques mois et je pense avoir enfin résolu le problème. Suivez le lien au bas de mon message si vous voulez juste voir le code.

La solution consiste à utiliser un UIWindow supplémentaire.

Quand vous voulez afficher votre UIAlertController:

  1. Faites de votre window la key et la window visible ( window.makeKeyAndVisible() )
  2. Utilisez simplement une instance UIViewController simple comme rootViewController de la nouvelle window. ( window.rootViewController = UIViewController() )
  3. Présentez votre UIAlertController sur le rootViewController de votre window

Quelques choses à noter:

  • Votre UIWindow doit être fortement référencée. Si ce n'est pas fortement référencé, il n'apparaîtra jamais (parce qu'il est publié). Je recommand d'utiliser une propriété, mais j'ai également eu du succès avec un object associé .
  • Pour s'assurer que la window apparaît au-dessus de tout le rest (y compris les controllers système UIAlertControllers), je définis le windowLevel. ( window.windowLevel = UIWindowLevelAlert + 1 )

Enfin, j'ai une implémentation complète si vous voulez juste regarder cela.

https://github.com/dbettermann/DBAlertController

En améliorant la réponse d'agilityvision , vous devrez créer une window avec un controller de vue racine transparent et présenter la vue d'alerte à partir de là.

Cependant, tant que vous avez une action dans votre controller d'alerte, vous n'avez pas besoin de garder une reference à la window . En tant que dernière étape du bloc du gestionnaire d'actions, vous devez simplement masquer la window dans le cadre de la tâche de nettoyage. En ayant une reference à la window dans le bloc gestionnaire, cela crée une reference circulaire temporaire qui serait rompue une fois que le controller d'alerte est fermé.

 UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; window.rootViewController = [UIViewController new]; window.windowLevel = UIWindowLevelAlert + 1; UIAlertController* alertCtrl = [UIAlertController alertControllerWithTitle:... message:... preferredStyle:UIAlertControllerStyleAlert]; [alertCtrl addAction:[UIAlertAction actionWithTitle:NSLocalizedSsortingng(@"OK",@"Generic confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { ... // do your stuff // very important to hide the window afterwards. // this also keeps a reference to the window until the action is invoked. window.hidden = YES; }]]; [window makeKeyAndVisible]; [window.rootViewController presentViewController:alertCtrl animated:YES completion:nil]; 

Cela fonctionne dans Swift pour les controllers de vue normale et même s'il y a un controller de navigation à l'écran:

 let alert = UIAlertController(...) let alertWindow = UIWindow(frame: UIScreen.mainScreen().bounds) alertWindow.rootViewController = UIViewController() alertWindow.windowLevel = UIWindowLevelAlert + 1; alertWindow.makeKeyAndVisible() alertWindow.rootViewController?.presentViewController(alert, animated: true, completion: nil) 

La solution suivante n'a pas fonctionné même si elle semblait très prometteuse avec toutes les versions. Cette solution génère un AVERTISSEMENT .

Attention: Essayez de présenter sur la vue de qui n'est pas dans la hiérarchie de la window!

https://stackoverflow.com/a/34487871/2369867 => Cela semble prometteur alors. Mais ce n'était pas dans Swift 3 . Donc, je réponds à cela dans Swift 3 et ce n'est pas un exemple de model.

C'est un code plutôt entièrement fonctionnel en soi une fois que vous collez dans n'importe quelle fonction.

Quick Swift 3 code autonome

 let alertController = UIAlertController(title: "<your title>", message: "<your message>", preferredStyle: UIAlertControllerStyle.alert) alertController.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.cancel, handler: nil)) let alertWindow = UIWindow(frame: UIScreen.main.bounds) alertWindow.rootViewController = UIViewController() alertWindow.windowLevel = UIWindowLevelAlert + 1; alertWindow.makeKeyAndVisible() alertWindow.rootViewController?.present(alertController, animated: true, completion: nil) 

Ceci est testé et le code de travail dans Swift 3.

En ajoutant à la réponse de Zev (et en revenant à Objective-C), vous pourriez vous refind dans une situation où votre controller de vue racine présente un autre VC via une segue ou autre chose. Appeler presentedViewController sur le VC racine prendra soin de ceci:

 [[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController presentViewController:alertController animated:YES completion:^{}]; 

Cela a résolu un problème que j'avais lorsque la racine VC s'était séparée d'un autre circuit virtuel, et au lieu de présenter le controller d'alerte, un avertissement comme ceux qui ont été rapportés ci-dessus a été émis:

 Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy! 

Je ne l'ai pas testé, mais cela peut aussi être nécessaire si votre VC racine est un controller de navigation.

Créer une extension comme dans Aviel Gross réponse. Ici vous avez l'extension Objective-C.

Ici vous avez un file d'en-tête * .h

 // UIAlertController+Showable.h #import <UIKit/UIKit.h> @interface UIAlertController (Showable) - (void)show; - (void)presentAnimated:(BOOL)animated completion:(void (^)(void))completion; - (void)presentFromController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion; @end 

Et implémentation: * .m

 // UIAlertController+Showable.m #import "UIAlertController+Showable.h" @implementation UIAlertController (Showable) - (void)show { [self presentAnimated:YES completion:nil]; } - (void)presentAnimated:(BOOL)animated completion:(void (^)(void))completion { UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController; if (rootVC != nil) { [self presentFromController:rootVC animated:animated completion:completion]; } } - (void)presentFromController:(UIViewController *)viewController animated:(BOOL)animated completion:(void (^)(void))completion { if ([viewController isKindOfClass:[UINavigationController class]]) { UIViewController *visibleVC = ((UINavigationController *)viewController).visibleViewController; [self presentFromController:visibleVC animated:animated completion:completion]; } else if ([viewController isKindOfClass:[UITabBarController class]]) { UIViewController *selectedVC = ((UITabBarController *)viewController).selectedViewController; [self presentFromController:selectedVC animated:animated completion:completion]; } else { [viewController presentViewController:self animated:animated completion:completion]; } } @end 

Vous utilisez cette extension dans votre file d'implémentation comme ceci:

 #import "UIAlertController+Showable.h" UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Title here" message:@"Detail message here" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}]; [alert addAction:defaultAction]; // Add more actions if needed [alert show]; 

Façon sténographique de présenter l'alerte en Objective-C:

 [[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil]; 

alertController est votre object UIAlertController .

REMARQUE: vous devrez également vous assurer que votre class d'assistance étend UIViewController

La réponse de Zev Eisenberg est simple et directe, mais elle ne fonctionne pas toujours, et elle peut échouer avec ce message d'avertissement:

 Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10> on <ThisViewController: 0x7fe6fb409480> which is already presenting <AnotherViewController: 0x7fe6fd109c00> 

C'est parce que le rootViewController de Windows n'est pas en haut des vues présentées. Pour corriger cela, nous devons remonter la string de présentation, comme indiqué dans mon code d'extension UIAlertController écrit dans Swift 3:

  /// show the alert in a view controller if specified; otherwise show from window's root pree func show(inViewController: UIViewController?) { if let vc = inViewController { vc.present(self, animated: true, completion: nil) } else { // find the root, then walk up the chain var viewController = UIApplication.shared.keyWindow?.rootViewController var presentedVC = viewController?.presentedViewController while presentedVC != nil { viewController = presentedVC presentedVC = viewController?.presentedViewController } // now we present viewController?.present(self, animated: true, completion: nil) } } func show() { show(inViewController: nil) } 

Mises à jour le 15/09/2017:

Testé et confirmé que la logique ci-dessus fonctionne toujours très bien dans la graine iOS 11 GM nouvellement disponible. Cependant, la méthode votée par agilityvision ne l'est pas: la vue d'alerte présentée dans une UIWindow nouvellement UIWindow trouve sous le keyboard et empêche potentiellement l'user d' UIWindow sur ses buttons. C'est parce que dans iOS 11, tous les windowLevels supérieurs à ceux de la window du keyboard sont abaissés à un niveau inférieur.

Un artefact de présentation à partir de keyWindow est cependant l'animation du keyboard qui glisse vers le bas lorsque l'alerte est présentée, et qui glisse de nouveau lorsque l'alerte est ignorée. Si vous voulez que le keyboard rest là pendant la présentation, vous pouvez essayer de présenter à partir de la window supérieure elle-même, comme indiqué dans le code ci-dessous:

 func show(inViewController: UIViewController?) { if let vc = inViewController { vc.present(self, animated: true, completion: nil) } else { // get a "solid" window with the highest level let alertWindow = UIApplication.shared.windows.filter { $0.tintColor != nil || $0.className() == "UIRemoteKeyboardWindow" }.sorted(by: { (w1, w2) -> Bool in return w1.windowLevel < w2.windowLevel }).last // save the top window's tint color let savedTintColor = alertWindow?.tintColor alertWindow?.tintColor = UIApplication.shared.keyWindow?.tintColor // walk up the presentation tree var viewController = alertWindow?.rootViewController while viewController?.presentedViewController != nil { viewController = viewController?.presentedViewController } viewController?.present(self, animated: true, completion: nil) // restore the top window's tint color if let tintColor = savedTintColor { alertWindow?.tintColor = tintColor } } } 

La seule partie less importante du code ci-dessus est qu'il vérifie le nom de la class UIRemoteKeyboardWindow pour s'assurer que nous pouvons l'inclure aussi. Néanless, le code ci-dessus fonctionne très bien avec les semences iOS 9, 10 et 11 GM, avec la bonne couleur de teinte et sans les artefacts de glissement du keyboard.

 extension UIApplication { /// The top most view controller static var topMostViewController: UIViewController? { return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController } } extension UIViewController { /// The visible view controller from a given view controller var visibleViewController: UIViewController? { if let navigationController = self as? UINavigationController { return navigationController.topViewController?.visibleViewController } else if let tabBarController = self as? UITabBarController { return tabBarController.selectedViewController?.visibleViewController } else if let presentedViewController = presentedViewController { return presentedViewController.visibleViewController } else { return self } } } 

Avec cela, vous pouvez facilement présenter votre alerte comme si

 UIApplication.topMostViewController?.present(viewController, animated: true, completion: nil) 

Une chose à noter est que si un UIAlertController est actuellement affiché, UIApplication.topMostViewController returnnera un UIAlertController . La présentation au-dessus d'un UIAlertController a un comportement étrange et devrait être évitée. En tant que tel, vous devez vérifier manuellement que !(UIApplication.topMostViewController is UIAlertController) avant de présenter, ou append un else if case pour renvoyer nil si self is UIAlertController

 extension UIViewController { /// The visible view controller from a given view controller var visibleViewController: UIViewController? { if let navigationController = self as? UINavigationController { return navigationController.topViewController?.visibleViewController } else if let tabBarController = self as? UITabBarController { return tabBarController.selectedViewController?.visibleViewController } else if let presentedViewController = presentedViewController { return presentedViewController.visibleViewController } else if self is UIAlertController { return nil } else { return self } } } 

Vous pouvez envoyer la vue ou le controller en cours en tant que paramètre:

 + (void)myUtilityMethod:(id)controller { // do stuff // something bad happened, display an alert. } 

Cross post ma réponse puisque ces deux threads ne sont pas marqués comme des dupes …

Maintenant que UIViewController fait partie de la string du répondeur, vous pouvez faire quelque chose comme ceci:

 if let vc = self.nextResponder()?.targetForAction(#selector(UIViewController.presentViewController(_:animated:completion:)), withSender: self) as? UIViewController { let alert = UIAlertController(title: "A snappy title", message: "Something bad happened", preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil)) vc.presentViewController(alert, animated: true, completion: nil) } 

Si quelqu'un est intéressé, j'ai créé une version Swift 3 de @agilityvision. Le code:

 import Foundation import UIKit extension UIAlertController { var window: UIWindow? { get { return objc_getAssociatedObject(self, "window") as? UIWindow } set { objc_setAssociatedObject(self, "window", newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } open override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) self.window?.isHidden = true self.window = nil } func show(animated: Bool = true) { let window = UIWindow(frame: UIScreen.main.bounds) window.rootViewController = UIViewController(nibName: nil, bundle: nil) let delegate = UIApplication.shared.delegate if delegate?.window != nil { window.tintColor = delegate!.window!!.tintColor } window.windowLevel = UIApplication.shared.windows.last!.windowLevel + 1 window.makeKeyAndVisible() window.rootViewController!.present(self, animated: animated, completion: nil) self.window = window } } 

Voici la réponse de mythicalcoder en extension, testée et fonctionnant dans Swift 4:

 extension UIAlertController { func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) { let alertWindow = UIWindow(frame: UIScreen.main.bounds) alertWindow.rootViewController = UIViewController() alertWindow.windowLevel = UIWindowLevelAlert + 1; alertWindow.makeKeyAndVisible() alertWindow.rootViewController?.present(self, animated: animated, completion: completion) } } 

Exemple d'utilisation:

 let alertController = UIAlertController(title: "<Alert Title>", message: "<Alert Message>", preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil)) alertController.presentInOwnWindow(animated: true, completion: { print("completed") }) 

Vous pouvez essayer d'implémenter une catégorie sur UIViewController avec mehtod comme - (void)presentErrorMessage; Et à l'intérieur de cette méthode, vous implémentez UIAlertController et vous le présentez sur self . Que dans votre code client, vous aurez quelque chose comme:

[myViewController presentErrorMessage];

In that way you'll avoid unneccessary parametrs and warnings about view not being in window hierarchy.

There 2 approaches that you can use:

-Use UIAlertView or 'UIActionSheet' instead (not recommended, cause it deprecated in iOS 8 but it works now)

-Somehow remember the last view controller which is presented. Here is example.

 @interface UIViewController (TopController) + (UIViewController *)topViewController; @end // implementation #import "UIViewController+TopController.h" #import <objc/runtime.h> static __weak UIViewController *_topViewController = nil; @implementation UIViewController (TopController) + (UIViewController *)topViewController { UIViewController *vc = _topViewController; while (vc.parentViewController) { vc = vc.parentViewController; } return vc; } + (void)load { [super load]; [self swizzleSelector:@selector(viewDidAppear:) withSelector:@selector(myViewDidAppear:)]; [self swizzleSelector:@selector(viewWillDisappear:) withSelector:@selector(myViewWillDisappear:)]; } - (void)myViewDidAppear:(BOOL)animated { if (_topViewController == nil) { _topViewController = self; } [self myViewDidAppear:animated]; } - (void)myViewWillDisappear:(BOOL)animated { if (_topViewController == self) { _topViewController = nil; } [self myViewWillDisappear:animated]; } + (void)swizzleSelector:(SEL)sel1 withSelector:(SEL)sel2 { Class class = [self class]; Method originalMethod = class_getInstanceMethod(class, sel1); Method swizzledMethod = class_getInstanceMethod(class, sel2); BOOL didAddMethod = class_addMethod(class, sel1, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, sel2, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } @end 

Usage:

 [[UIViewController topViewController] presentViewController:alertController ...]; 

Kevin Sliech provided a great solution.

I now use the below code in my main UIViewController subclass.

One small alteration i made was to check to see if the best presentation controller is not a plain UIViewController. If not, it's got to be some VC that presents a plain VC. Thus we return the VC that's being presented instead.

 - (UIViewController *)bestPresentationController { UIViewController *bestPresentationController = [UIApplication sharedApplication].keyWindow.rootViewController; if (![bestPresentationController isMemberOfClass:[UIViewController class]]) { bestPresentationController = bestPresentationController.presentedViewController; } return bestPresentationController; } 

Seems to all work out so far in my testing.

Thank you Kevin!

I use this code with some little personal variations in my AppDelegate class

 -(UIViewController*)presentingRootViewController { UIViewController *vc = self.window.rootViewController; if ([vc isKindOfClass:[UINavigationController class]] || [vc isKindOfClass:[UITabBarController class]]) { // filter nav controller vc = [AppDelegate findChildThatIsNotNavController:vc]; // filter tab controller if ([vc isKindOfClass:[UITabBarController class]]) { UITabBarController *tbc = ((UITabBarController*)vc); if ([tbc viewControllers].count > 0) { vc = [tbc viewControllers][tbc.selectedIndex]; // filter nav controller again vc = [AppDelegate findChildThatIsNotNavController:vc]; } } } return vc; } /** * Private helper */ +(UIViewController*)findChildThatIsNotNavController:(UIViewController*)vc { if ([vc isKindOfClass:[UINavigationController class]]) { if (((UINavigationController *)vc).viewControllers.count > 0) { vc = [((UINavigationController *)vc).viewControllers objectAtIndex:0]; } } return vc; } 

In addition to great answers given ( agilityvision , adib , malhal ). To reach queueing behaviour like in good old UIAlertViews (avoid alert windows overlap), use this block to observe window level availability:

 @interface UIWindow (WLWindowLevel) + (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block; @end @implementation UIWindow (WLWindowLevel) + (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block { UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; if (keyWindow.windowLevel == level) { // window level is occupied, listn for windows to hide id observer; observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIWindowDidBecomeHiddenNotification object:keyWindow queue:nil usingBlock:^(NSNotification *note) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; [self notifyWindowLevelIsAvailable:level withBlock:block]; // recursive retry }]; } else { block(); // window level is available } } @end 

Complete example:

 [UIWindow notifyWindowLevelIsAvailable:UIWindowLevelAlert withBlock:^{ UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; alertWindow.windowLevel = UIWindowLevelAlert; alertWindow.rootViewController = [UIViewController new]; [alertWindow makeKeyAndVisible]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:nil preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { alertWindow.hidden = YES; }]]; [alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil]; }]; 

This will allow you to avoid alert windows overlap. Same method can be used to separate and put in queue view controllers for any number of window layers.

Seems to work:

 static UIViewController *viewControllerForView(UIView *view) { UIResponder *responder = view; do { responder = [responder nextResponder]; } while (responder && ![responder isKindOfClass:[UIViewController class]]); return (UIViewController *)responder; } -(void)showActionSheet { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]]; [alertController addAction:[UIAlertAction actionWithTitle:@"Do it" style:UIAlertActionStyleDefault handler:nil]]; [viewControllerForView(self) presentViewController:alertController animated:YES completion:nil]; } 

create helper class AlertWindow and than use as

 let alertWindow = AlertWindow(); let alert = UIAlertController(title: "Hello", message: "message", preferredStyle: .alert); let cancel = UIAlertAction(title: "Ok", style: .cancel){(action) in //.... action code here // reference to alertWindow retain it. Every action must have this at end alertWindow.isHidden = true; // here AlertWindow.deinit{ } } alert.addAction(cancel); alertWindow.present(alert, animated: true, completion: nil) class AlertWindow:UIWindow{ convenience init(){ self.init(frame:UIScreen.main.bounds); } override init(frame: CGRect) { super.init(frame: frame); if let color = UIApplication.shared.delegate?.window??.tintColor { tintColor = color; } rootViewController = UIViewController() windowLevel = UIWindowLevelAlert + 1; makeKeyAndVisible() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit{ // semaphor.signal(); } func present(_ ctrl:UIViewController, animated:Bool, completion: (()->Void)?){ rootViewController!.present(ctrl, animated: animated, completion: completion); } } 

@agilityvision's answer is so good. I have sense used in swift projects so I thought I would share my take on his answer using swift 3.0

 fileprivate class MyUIAlertController: UIAlertController { typealias Handler = () -> Void struct AssociatedKeys { static var alertWindowKey = "alertWindowKey" } dynamic var _alertWindow: UIWindow? var alertWindow: UIWindow? { return objc_getAssociatedObject(self, &AssociatedKeys.alertWindowKey) as? UIWindow } func setAlert(inWindow window: UIWindow) { objc_setAssociatedObject(self, &AssociatedKeys.alertWindowKey, _alertWindow, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } func show(completion: Handler? = nil) { show(animated: true, completion: completion) } func show(animated: Bool, completion: Handler? = nil) { _alertWindow = UIWindow(frame: UIScreen.main.bounds) _alertWindow?.rootViewController = UIViewController() if let delegate: UIApplicationDelegate = UIApplication.shared.delegate, let window = delegate.window { _alertWindow?.tintColor = window?.tintColor } let topWindow = UIApplication.shared.windows.last _alertWindow?.windowLevel = topWindow?.windowLevel ?? 0 + 1 _alertWindow?.makeKeyAndVisible() _alertWindow?.rootViewController?.present(self, animated: animated, completion: completion) } fileprivate override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) _alertWindow?.isHidden = true _alertWindow = nil } } 

Register for a notification prior to calling the class method.

Swift code:

 NSNotificationCenter.defaultCenter().addObserver(self, selector: "displayAlert", name: "ErrorOccured", object: nil) 

In the displayAlert instance method you could display your alert.