2015-03-25 12 views
17

Ho appena creato un progetto di applicazione di visualizzazione singola con classe ViewController. Mi piacerebbe mostrare un UIAlertController da una funzione che si trova all'interno della mia stessa classe.AlertController non è nella gerarchia di finestre

Ecco la mia classe con un avviso.

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

Qui è ViewController che esegue l'avviso.

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

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

Questo è ciò che ottengo invece di una bella allerta.

Attenzione: tentativo di presentare UIAlertController: 0x797d2d20 on Sprint1.AlertController: 0x797cc500 la cui vista non è nella gerarchia delle finestre!

Cosa devo fare?

+0

c'è un motivo per cui non stai usando self.show()? – Alex

+0

Dove dovrei usare questa funzione? – wtznc

+2

invece di self.presentViewController (alert, animato: true, completion: nil) usa self.show() – Alex

risposta

11

Esaminiamo la gerarchia della vista. Hai un ViewController. Quindi si sta creando un AlertController, non lo si aggiunge alla gerarchia e si sta chiamando un metodo di istanza su di esso, che tenta di utilizzare il AlertController come controller di presentazione per mostrare solo un altro controller (UIAlertController).

+ ViewController 
    + AlertController (not in hierarchy) 
     + UIAlertController (cannot be presented from AlertController) 

Per semplificare il codice

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

    @IBAction func showAlertButton(sender: AnyObject) { 
     var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert) 
     self.presentViewController(alert, animated: true, completion: nil) 
    } 
} 

Questo funzionerà.

Se è necessario il AlertController per qualcosa, è necessario aggiungerlo prima alla gerarchia, ad es. utilizzando addChildViewController o utilizzando un'altra chiamata presentViewController.

Se si desidera che la classe di essere solo un aiuto per la creazione di avviso, dovrebbe assomigliare a questo:

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

chiamato come

var alert = AlertHelper() 
alert.showAlert(fromController: self) 
+0

Ho bisogno di' AlertControl perché devo fare qualche altra cosa in questa classe. Ho provato 'self.addChildViewController (alert) self.presentViewController (alert, animato: true, completion: nil)' ma si blocca quando viene toccato il pulsante. Motivo: "L'applicazione ha tentato di presentare un controller attivo modally ." – wtznc

+0

@wtznc Che cosa esattamente devi fare lì? Il tuo problema è ovviamente di architettura. – Sulthan

+0

Devo visualizzare questo avviso dopo un ritardo, a seconda della risposta dal back-end. – wtznc

2

Se si desidera creare una classe separata per la visualizzazione avviso come questo, sottoclasse NSObject non UIViewController.

E passare il riferimento ViewControllers da cui è stato avviato, alla funzione showAlert in modo che è possibile presentare la visualizzazione di avviso lì.

3

Scrivi le seguenti 3 righe, tutto ciò che dobbiamo fare è questo.

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) 
    } 
+0

Questo ha funzionato bene per me. L'ho implementato nella mia ... classe di utilità e presto! – user462990

+4

Con questo ci sono ancora situazioni in cui la vista rootViewControllers è rilasciata (quando una vc modale è presentata davanti ad essa, ad es.) E il tuo avviso non verrà mostrato. Riceverai l'errore 'Avvertenza: Tentativo di presentare su la cui vista non è nella gerarchia della finestra!' –

16

Se stai instancing vostro UIAlertController da un controller modale, è necessario farlo in viewDidAppear, non in viewDidLoad o riceverai un errore.

Ecco il mio codice (Swift 3):

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) 
} 
+0

Grazie, la soluzione ha funzionato ... ma mi chiedo il motivo per cui non funziona in viewDidLoad ... – ioopl

+0

@ioopl Immagino che questo serva a impedire che gli avvisi vengano visualizzati prima di viewcontroller o durante l'animazione. – Skoua

1

Ecco il codice di un UIAlertController in una classe Utility.swift (non un UIViewController) in Swift3, grazie 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) 
} 
+3

Con questo ci sono ancora situazioni in cui la vista rootViewControllers è rilasciata (quando una vc modale è presentata davanti ad essa, ad es.) E il tuo avviso non verrà mostrato. Riceverai l'errore 'Avvertenza: Tentativo di presentare su la cui vista non è nella gerarchia della finestra!' –

3

È possibile utilizzare sotto la funzione di chiamata di avviso da ogni dove basta includere questi metodo 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); 
    } 

Quindi chiamare

AnyClass.alert(message:"Your Message") 
0

Mi ha aiutato a restare un leggero ritardo tra la metodo viewDidLoad e attivazione del metodo di avviso:

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