web-dev-qa-db-fra.com

AlertController n'est pas dans la hiérarchie des fenêtres

Je viens de créer un projet d'application à vue unique avec la classe ViewController. Je voudrais montrer un UIAlertController à partir d'une fonction située dans ma propre classe. 

Voici ma classe avec une alerte.

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

Voici ViewController qui exécute l'alerte.

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

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

C'est ce que je reçois au lieu d'une belle alerte.

Avertissement: Essayez de présenter UIAlertController: 0x797d2d20 sur Sprint1.AlertController: 0x797cc500 dont la vue ne figure pas dans la hiérarchie des fenêtres!

Que devrais-je faire?

30
wtznc

Si vous instanciez votre UIAlertController à partir d'un contrôleur modal, vous devez le faire dans viewDidAppear, pas dans viewDidLoad ou vous obtiendrez une erreur.

Voici mon 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)
}
33
Skoua

Vous pouvez utiliser la fonction ci-dessous pour appeler des alertes depuis n’importe quel lieu. Il suffit d’inclure ces méthodes dans 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);
    }

Alors appelez

AnyClass.alert(message:"Your Message")
5
Varun Naharia

Écrivez les 3 lignes suivantes, tout ce que nous avons à faire est la suivante.

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)
  }
4
Mitsuaki Ishimoto

Si vous souhaitez créer une classe distincte pour afficher une alerte de ce type, sous-classe NSObject et non UIViewController.

Et passez la référence ViewControllers à partir de laquelle il est lancé à la fonction showAlert afin que vous puissiez y afficher la vue des alertes.

2
Prajeet Shrestha

Voici le code d'un UIAlertController dans une classe Utility.Swift (pas un UIViewController) dans Swift3, merci 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)
}
1
user462990

Cela a fonctionné pour moi:

- (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];
}

La mise en oeuvre: 

UIViewController * topViewController = [self topViewController];

Utilisation avec alerte: 

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

Vous pouvez envoyer une alerte depuis n'importe quelle classe de votre application (utilisant UIKit: #import <UIKit/UIKit.h>). 

Source ici.

0
Pedro Trujillo

Cela m'a aidé à laisser un léger délai entre la méthode viewDidLoad et l'activation de la méthode d'alerte:

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