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
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.
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)
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........
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!")
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é).
Dans Swift 3:
UIApplication.shared.keyWindow?.rootViewController?.present(alertView, animated: true, completion: nil)
Pour Swift 4 et plus
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
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)
}
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
}
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];
}
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.