如何从 appDelegate 呈现 UIAlertView

Posted

技术标签:

【中文标题】如何从 appDelegate 呈现 UIAlertView【英文标题】:How to present UIAlertView from appDelegate 【发布时间】:2015-01-06 21:01:08 【问题描述】:

我正在尝试显示来自 appDelegate 的 UIAlertView didReceiveRemoteNotification 当应用收到推送通知时。

我有这个错误:

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

这是我的代码:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: NSDictionary) 

    var contentPush: NSDictionary = userInfo.objectForKey("aps") as NSDictionary

    var message = contentPush.objectForKey("alert") as String



    let alertController = UIAlertController(title: "Default Style", message: message, preferredStyle: .Alert)

    let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel)  (action) in
                    // ...
    
    alertController.addAction(cancelAction)

    let OKAction = UIAlertAction(title: "OK", style: .Default)  (action) in

        let photoPushedVc = self.storyboard.instantiateViewControllerWithIdentifier("CommentTableViewController") as CommentTableViewController

        println("the fetched post is \(post)")

        photoPushedVc.post = post

        let activeVc = UIApplication.sharedApplication().keyWindow?.rootViewController

        activeVc?.presentViewController(photoPushedVc, animated: true, completion: nil)
   

   alertController.addAction(OKAction)

   let activeVc = UIApplication.sharedApplication().keyWindow?.rootViewController


   activeVc?.presentViewController(alertController, animated: true, completion: nil)

【问题讨论】:

你是否试图让它从根视图控制器显示? 正如您在我的代码中看到的那样,我正在尝试使用 let activeVc = UIApplication.sharedApplication().keyWindow?.rootViewController 从活动 VC 中对它进行反播放 请注意,我的应用中有一个 tabBarController。 为什么你认为“rootViewController”必然是“活跃的VC”? 我在想.keyWindow?给出“活跃的VC”。如果没有,你怎么得到它? 【参考方案1】:

使用 Objective-CAppDelegate 生成 AlertController 对话框,

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"Hello World!" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:ok];

类型 1

UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
alertWindow.rootViewController = [[UIViewController alloc] init];
alertWindow.windowLevel = UIWindowLevelAlert + 1;
[alertWindow makeKeyAndVisible];
[alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil];

类型 2

UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
while (topController.presentedViewController) 
    topController = topController.presentedViewController;

[topController presentViewController:alertController animated:YES completion:nil];

两者都经过测试并且工作正常。

【讨论】:

有时会起作用,但 Pavel 的回答是更好的解决方案。 Type 1 适用于任何情况。类型 2 可能不适用于某些设备。【参考方案2】:

好的,我终于明白了,你需要在尝试展示你的 alertController 之前使用它找到活动的 VC:

let navigationController = application.windows[0].rootViewController as UINavigationController
let activeViewCont = navigationController.visibleViewController
activeViewCont.presentViewController(alertController, animated: true, completion: nil)

【讨论】:

您还应该检查是否 activeVC 尚未呈现,否则将无法正常工作。通过检查 activeViewCont.presentedViewController 是否返回 nil【参考方案3】:

这是我的 Swift 3.0 示例

func showTopLevelAlert() 
    let alertController = UIAlertController (title: "title", message: "message.", preferredStyle: .alert)

    let firstAction = UIAlertAction(title: "First", style: .default, handler: nil)
    alertController.addAction(firstAction)

    let cancelAction = UIAlertAction(title: "Отмена", style: .cancel, handler: nil)
    alertController.addAction(cancelAction)

    let alertWindow = UIWindow(frame: UIScreen.main.bounds)

    alertWindow.rootViewController = UIViewController()
    alertWindow.windowLevel = UIWindowLevelAlert + 1;
    alertWindow.makeKeyAndVisible()

    alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)


希望对大家有所帮助

【讨论】:

这看起来不错。提醒一下:不要忘记在主线程中完成所有这些操作,否则它会在alertWindow.makeKeyAndVisible() 崩溃。 UIWindow 将如何被释放? @meaning-matters 将在您关闭 UIAlertController 对话框时释放【参考方案4】:

如果你在 Objective-c 中需要同样的东西

UIAlertController *alertvc = [UIAlertController alertControllerWithTitle:@"Alert Title..!!" message:@"Hey! Alert body come here." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *actionOk = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) 

];

[alertvc addAction:actionOk];

    如果您有基于导航的应用:

    UINavigationController *nvc = (UINavigationController *)[[application windows] objectAtIndex:0].rootViewController;
    UIViewController *vc = nvc.visibleViewController;
    [vc presentViewController:alertvc animated:YES completion:nil];
    

    如果您有基于单一视图的应用程序:

    UIViewController *vc = self.window.rootViewController;
    [vc presentViewController:alertvc animated:YES completion:nil];
    

【讨论】:

如何使用单视图应用程序(不是基于导航控制器的应用程序)做到这一点。 @Supertecnoboff 请检查我的第二个答案。 (答案已更新。)【参考方案5】:

我是怎么做到的

func showAlertAppDelegate(title : String,message : String,buttonTitle : String,window: UIWindow)
    let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
    alert.addAction(UIAlertAction(title: buttonTitle, style: UIAlertActionStyle.Default, handler: nil))
    window.rootViewController?.presentViewController(alert, animated: true, completion: nil)

使用示例

self.showAlertAppDelegate(title: "Alert",message: "Opened From AppDelegate",buttonTitle: "ok",window: self.window!);

Download Example With Source Code

【讨论】:

【参考方案6】:

通过应用代理在顶部控制器上显示警报

var topController : UIViewController = (application.keyWindow?.rootViewController)!

    while ((topController.presentedViewController) != nil) 
        topController = topController.presentedViewController!
    

    //showAlertInViewController func is in UIAlertController Extension
    UIAlertController.showAlertInViewController(topController, withMessage: messageString, title: titleString)

在 UIAlertController 中添加扩展

    static func showAlertInViewController(viewController: UIViewController?, withMessage message: String, title: String) 
    let myAlert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)

    myAlert.addAction(UIAlertAction(title: NSLocalizedString("Ok", comment: ""), style: .Default, handler:  (action: UIAlertAction!) in
        print("Handle Ok logic here")

        // Let the alert simply dismiss for now
    ))

    viewController?.presentViewController(myAlert, animated: true, completion: nil)

【讨论】:

【参考方案7】:

我使用 UIViewController 扩展来获取当前可见的视图控制器(参见:How to get visible viewController from app delegate when using storyboard?)。

然后呈现警报控制器:

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

【讨论】:

以上是关于如何从 appDelegate 呈现 UIAlertView的主要内容,如果未能解决你的问题,请参考以下文章

如何从 appdelegate xcode 11 swift 5 呈现视图控制器

目标 C:如何从 appdelegate 呈现模态视图控制器?

如何根据 iOS 中的两个条件从 AppDelegate 呈现两个不同的 ViewController?

如何通过 TabBarController => NavbarController 从 Appdelegate 呈现视图控制器

从 appDelegate 呈现 ViewController

当应用程序准备好呈现警报时,从 AppDelegate 呈现警报