web-dev-qa-db-fra.com

Afficher UIAlertController de la classe UIView/NSObject

J'ai working iOS application Afin de support iOS8, je remplace UIAlertView/UIActionSheet with UIAlertController.

Problème:
Pour l'affichage, UIAlertController, j'ai besoin de la méthode presentViewController De la classe UIViewController.
Mais UIAlertView est affiché à partir des classes qui sont inherited à partir de UIView or NSObject
Je ne peux pas obtenir la méthode [self presentViewController...] pour une raison évidente.

Mon travail :
J'ai essayé de récupérer la fenêtre courante du formulaire rootViewController et d’afficher UIAlertController.

[[[UIApplication sharedApplication] keyWindow].rootViewController presentViewController ...]

mais j'ai des problèmes de rotation, par exemple si mon contrôleur de vue actuel ne prend pas en charge la rotationit tournera si UIAlertController est ouvert.

Question:
Quelqu'un at-il rencontré le même problème et obtenu une solution sûre? 
si oui, donnez-moi un exemple ou un guide

21
Jageen

On dirait que vous êtes actuellement (avant iOS8) en train de déclencher une vue d'alerte à partir de votre objet de vue. C'est une pratique plutôt mauvaise, car en général, les alertes doivent être déclenchées par des actions et par la logique. Et ce code devrait vivre dans les contrôleurs.

Je vous suggère de refactoriser votre code actuel pour déplacer la logique qui déclenche l'alerte vers le bon contrôleur, puis vous pourrez facilement effectuer une mise à niveau vers iOS 8 en utilisant self comme contrôleur.

Si, au lieu de cela, vous appelez l'alerte depuis un objet extérieur, transmettez le contrôleur à la méthode qui appelle l'alerte. Quelque part en amont, vous devez connaître le contrôleur.

8
Rikkles

J'ai résolu un problème essentiellement similaire aujourd'hui. Comme Jageen , je me suis retrouvé dans une situation où je souhaitais présenter un contrôleur UIAlertController mais issu d'une classe non UIViewController. Dans mon cas, je voulais qu'une alerte apparaisse lorsque le bloc d'échec d'une requête HTTP est exécuté. 

C'est ce que j'ai utilisé et contrairement à notre ami ici, cela a fonctionné parfaitement pour moi. 

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(errorAlert, animated: true, completion: nil)
42
Lester

La meilleure solution pour les classes UIView est la suivante

Objectif c

UIViewController *currentTopVC = [self currentTopViewController];
currentTopVC.presentViewController......... 

- (UIViewController *)currentTopViewController
{
    UIViewController *topVC = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
    while (topVC.presentedViewController)
    {
        topVC = topVC.presentedViewController;
    }
    return topVC;
}

Rapide

var topVC = UIApplication.sharedApplication().keyWindow?.rootViewController
while((topVC!.presentedViewController) != nil){
     topVC = topVC!.presentedViewController
}
topVC?.presentViewController........
13
Shamsudheen TK

Ma solution est ci-dessous:

Rapide

class alert {
    func msg(message: String, title: String = "")
    {
        let alertView = UIAlertController(title: title, message: message, preferredStyle: .Alert)

        alertView.addAction(UIAlertAction(title: "Done", style: .Default, handler: nil))

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

Voici un exemple d'utilisation:

let Alert = alert()
Alert.msg("My alert (without title)")
Alert.msg("This is my alert", title: "Warning!")
11
fattihkoca

J'ai eu une situation où une sous-vue contient un bouton pour le fermer. Je présente une alerte pour confirmer l'action. Il envoie un message au délégué - qui est le contrôleur de vue contenant la sous-vue - pour supprimer la sous-vue.

A l'origine, j'ai présenté UIAlertView d'un UIView. Refactoring pour UIAlertController, étant donné que UIAlertController ne peut pas se présenter comme un UIAlertView, j'ai proposé ce qui suit (dans Swift; facilement traduit en ObjC):

Ajouter un protocole à la sous-vue:

protocol MySubviewDelegate {

    // called when user taps subview/delete button
    //   or, you could call it from a gesture handler, etc.
    func displayAlert(alert : UIAlertController)

    // called when user confirms delete from the alert controller
    func shouldRemoveSubview(sender : AnyObject)

}

Ajouter un délégué pour la sous-vue, et ajouter un gestionnaire pour le bouton/geste, tapez sur:

class MySubview : UIView {

    var subviewDelegate : MySubviewDelegate!

    ...

    func handleTap(sender : AnyObject) {

        // set up the alert controller here
        var alert = UIAlertController(title: "Confirm Delete", 
            message: "This action is permanent. Do you wish to continue?", 
            preferredStyle: UIAlertControllerStyle.Alert)

        // Cancel action 
        //   nil handler means "no action if Cancel button selected"
        alert.addAction(UIAlertAction(title: "Cancel",
            style: UIAlertActionStyle.Cancel,
            handler: nil))

        // Confirm action
        alert.addAction(UIAlertAction(title: "Confirm",
            style: UIAlertActionStyle.Default,
            handler: { (action : UIAlertAction!) -> Void in

                // call delegate method to perform confirmed action, - i.e. remove
                self.subviewDelegate.shouldRemoveSubview(self)
        }))

        // call delegate method to display alert controller
        //   send alert object to delegate
        self.subviewDelegate.displayAlert(alert)
    }
}

Définissez le UIViewController appelant en tant que délégué de la sous-vue, par exemple dans sa méthode viewDidLoad (), et incluez les méthodes de protocole:

class viewController : UIViewController, MySubviewDelegate {

    override func viewDidLoad() {

        super.viewDidLoad()

        self.subviewDelegate = self

        ...
    }

    func displayAlert(alert : UIAlertController) {

        presentViewController(alert, animated: true, completion: nil)
    }

    func shouldRemoveSubview(sender : AnyObject) {

        // cast as UIView / MySubview subclass
        var subview = sender as MySubview

       // remove the subview / perform the desired action
       subview.removeFromSuperview()

       ...
    }

  ...
}

Cela évite de rechercher le contrôleur de vue le plus élevé ou de transmettre des références aux contrôleurs de vue en sous-vues (autre que dans une relation objet/délégué).

3
Adrian Mannella

Dans Swift 3:

UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
2
Md. Najmul Hasan

Pour Swift 4 et plus

UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
2
raed

En général, les alertes doivent être traitées dans le contrôleur de vue. Voici un exemple du code requis:

Swift 3

private func displayError(message: String) {
    let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
    let okayAction = UIAlertAction(title: "Okay", style: .default, handler: nil)
    alertController.addAction(okayAction)
    present(alertController, animated: true, completion: nil)
}
0
Enfei L

Je sais que la question a déjà été répondue ... Mais comme je cherche aussi le même problème, mais aucune des solutions ci-dessus n’a fonctionné pour moi.

Ainsi, après de nombreux essais et erreurs, j'ai finalement trouvé une solution très simple et durable.

    func showError(title: String?, error: String?) {

    DispatchQueue.main.async(execute: {

        let alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.alert)

        alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))

        CommonMethods.instance.topMostController()?.present(alert, animated: true, completion: nil)

    })
}

static let instance = CommonMethods()

fileprivate func topMostController() -> UIViewController? {

    var presentedVC = UIApplication.shared.keyWindow?.rootViewController
    while let pVC = presentedVC?.presentedViewController {
        presentedVC = pVC
    }

    if presentedVC == nil {  }
    return presentedVC
}
0
Meet Patel

Pour Display UIAlertController in NSObject Class, utilisez la référence ci-dessous.

    UIAlertController * popup =   [UIAlertController
                              alertControllerWithTitle:nil
                              message:nil
                              preferredStyle:UIAlertControllerStyleActionSheet];

    UIAlertAction* cancel = [UIAlertAction
                             actionWithTitle:@"Cancel"
                             style:UIAlertActionStyleCancel
                             handler:^(UIAlertAction * action) {
                                 [popup dismissViewControllerAnimated:YES completion:nil];
                             }];
    [popup addAction:cancel];

    UIViewController *rootViewController = [[Helper shareInstance] topViewController];
    [rootViewController presentViewController:popup animated:YES completion:nil];

// Mettez la méthode ci-dessous dans votre classe Global Helper.

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

J'ai eu ce problème. Voir ma SO answer here pour le code permettant d’obtenir le meilleur contrôleur de vue avec lequel présenter un autre contrôleur de vue. 

Je conviens que dans la plupart des cas, il est déconseillé de présenter un contrôleur de vue à partir d'un objet qui n'est pas un contrôleur de vue, mais il est parfois nécessaire de le faire.

0
nvrtd frst