如何从 Appdelegate 显示 UIAlertController

Posted

技术标签:

【中文标题】如何从 Appdelegate 显示 UIAlertController【英文标题】:How to show UIAlertController from Appdelegate 【发布时间】:2016-03-22 13:31:25 【问题描述】:

我正在 ios 应用上使用 PushNotification。我想在应用收到通知时显示 UIalertcontroller。

我在 AppDelegate 中尝试以下代码:

[self.window.rootViewController presentViewController:alert animated:YES completion:nil];

但 UIAlertcontroller 显示在根视图(第一个屏幕)中,对于其他 uiviewcontroller,我收到警告或应用程序崩溃。

【问题讨论】:

崩溃报告是什么 但是 UIAlertcontroller 显示在根视图中......当然您正在将警报添加到根控制器。当然,它会在其他 uiview 控制器上崩溃,因为您正在尝试在控制器上添加警报,而该警报未向用户显示。 是的,我知道我正在将 uialertcontroller 添加到 rootView 而不是活动视图,我的问题是如何在收到通知时在另一个 uiviewcontroller 中显示 uialertController。 【参考方案1】:

试试这个

Objective-C

UIWindow* topWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
topWindow.rootViewController = [UIViewController new];
topWindow.windowLevel = UIWindowLevelAlert + 1;

UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"APNS" message:@"received Notification" preferredStyle:UIAlertControllerStyleAlert];

[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK",@"confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) 
    // continue your work

    // important to hide the window after work completed.
    // this also keeps a reference to the window until the action is invoked.
    topWindow.hidden = YES; // if you want to hide the topwindow then use this
    topWindow = nil; // if you want to remove the topwindow then use this 
]];

[topWindow makeKeyAndVisible];
[topWindow.rootViewController presentViewController:alert animated:YES completion:nil];

Swift3 及以上版本

var topWindow: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
topWindow?.rootViewController = UIViewController()
topWindow?.windowLevel = UIWindow.Level.alert + 1

let alert = UIAlertController(title: "APNS", message: "received Notification", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel)  _ in
    // continue your work

    // important to hide the window after work completed.
    // this also keeps a reference to the window until the action is invoked.
    topWindow?.isHidden = true // if you want to hide the topwindow then use this
    topWindow = nil // if you want to hide the topwindow then use this
 )

topWindow?.makeKeyAndVisible()
topWindow?.rootViewController?.present(alert, animated: true, completion: nil)

详细说明:http://www.thecave.com/2015/09/28/how-to-present-an-alert-view-using-uialertcontroller-when-you-dont-have-a-view-controller/

【讨论】:

我将如何为 swift 3 添加另一个按钮到 UIAlert 创建UIAlertAction的对象并处理动作 @JohnyDGood - 查看此内容以了解多项操作***.com/questions/35152650/… @Anbu.karthik 我想创建一个新窗口来显示警报不是一个好方法,代码只是隐藏窗口而不是完全删除它。如果我错了,你能纠正我吗? @TheTiger-感谢前辈的宝贵意见,我根据我回答的ARC概念更新了答案【参考方案2】:

最短且最简单的:

创建一个扩展:

extension UIApplication 
class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? 
    if let navigationController = controller as? UINavigationController 
        return topViewController(controller: navigationController.visibleViewController)
    
    if let tabController = controller as? UITabBarController 
        if let selected = tabController.selectedViewController 
            return topViewController(controller: selected)
        
    
    if let presented = controller?.presentedViewController 
        return topViewController(controller: presented)
    
    return controller
 

然后在任何地方使用它

UIApplication.topViewController()?.present(UIViewController, animated: true, completion: nil)

有了它,您可以在任何地方显示警报或任何内容 示例:

let alert = UIAlertController(title: "Your title", message: "Your message", preferredStyle: .alert)
let cancelButton = UIAlertAction(title: "Ok", style: .cancel, handler: nil)
alert.addAction(cancelButton)
UIApplication.topViewController()?.present(alert, animated: true, completion: nil)

替代方法:

无需创建任何扩展或任何方法或任何东西,只需编写以上 3 行来创建警报和呈现使用:

self.window?.rootViewController?.present(alert, animated: true, completion: nil)

就是这样! =)

【讨论】:

【参考方案3】:

Anbu.Karthik's answer 但在 斯威夫特 4.1

var topWindow: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
topWindow?.rootViewController = UIViewController()
topWindow?.windowLevel = UIWindowLevelAlert + 1
let alert: UIAlertController =  UIAlertController(title: "APNS", message: "received Notification", preferredStyle: .alert)
    alert.addAction(UIAlertAction.init(title: "OK", style: .default, handler:  (alertAction) in
        topWindow?.isHidden = true
        topWindow = nil
    ))

topWindow?.makeKeyAndVisible()
topWindow?.rootViewController?.present(alert, animated: true, completion:nil)

感谢您阅读本文。

【讨论】:

【参考方案4】:

斯威夫特 4.1 您可以使用以下代码从 AppDelegate 显示警报

func showAlertFromAppDelegates()
    let alertVC = UIAlertController(title: "Oops" , message: "Presented Alert from AppDelegates", preferredStyle: UIAlertControllerStyle.alert)
    let okAction = UIAlertAction(title: "Okay", style: UIAlertActionStyle.cancel)  (alert) in
        exit(0) // Your code here
    
    alertVC.addAction(okAction)
    DispatchQueue.main.async 
        var presentVC = self.window?.rootViewController
        while let next = presentVC?.presentedViewController 
            presentVC = next
        
        presentVC?.present(alertVC, animated: true, completion: nil)
    

【讨论】:

这对我很有用!起初我认为指令是将这个函数放在实际的 appDelegate 中,但我能够将它放在启动图像上传的视图控制器中,这使我能够在他们关闭该视图控制器后显示此警报。谢谢! 警报出现两次【参考方案5】:

为了方便,我使用了分类

UIAlertController+UIWindow.h

@interface UIAlertController (UIWindow)

- (void)show;
- (void)show:(BOOL)animated;

@end

UIAlertController+UIWindow.m

#import <objc/runtime.h>

@interface UIAlertController (Private)

@property (nonatomic, strong) UIWindow *alertWindow;

@end

@implementation UIAlertController (Private)

@dynamic alertWindow;

- (void)setAlertWindow:(UIWindow *)alertWindow 
    objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);


- (UIWindow *)alertWindow 
    return objc_getAssociatedObject(self, @selector(alertWindow));


@end

@implementation UIAlertController (UIWindow)

- (void)show 
    [self show:YES];


- (void)show:(BOOL)animated 
    [self setupWindow];
    [self.alertWindow.rootViewController presentViewController:self animated:animated completion:nil];


- (void)setupWindow 
    self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.alertWindow.rootViewController = [[UIViewController alloc] init];

    id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
    if ([delegate respondsToSelector:@selector(window)]) 
        self.alertWindow.tintColor = delegate.window.tintColor;
    

    UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
    self.alertWindow.windowLevel = topWindow.windowLevel + 1;

    [self.alertWindow makeKeyAndVisible];


- (void)viewDidDisappear:(BOOL)animated 
    [super viewDidDisappear:animated];

    // precaution to insure window gets destroyed
    self.alertWindow.hidden = YES;
    self.alertWindow = nil;

使用

UIAlertController *alertController;
// -- code --
[alertController show];

【讨论】:

【参考方案6】:

我写了一个静态类,让代码在 swift 4 中可重用,这个类提供了不同的方法来显示警报。

        class AlertUtility: NSObject 

        static func showAlert(title: String!, message : String!, viewController: UIViewController) 
            let alert = UIAlertController(title: title, message: message ,preferredStyle: UIAlertControllerStyle.alert)

            alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "ok"), style: UIAlertActionStyle.cancel, handler: nil))

            viewController.present(alert, animated: true, completion: nil)
        

        static func showAlertAutoDismiss(title: String!, message : String!) -> Void 
            //let appDelegate = UIApplication.shared.delegate as! AppDelegate

            // the alert view
            let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)

            let topWindow = UIWindow(frame: UIScreen.main.bounds)
            topWindow.rootViewController = UIViewController()
            topWindow.windowLevel = UIWindowLevelAlert + 0.8

            topWindow.makeKeyAndVisible()
            topWindow.rootViewController?.present(alert, animated: true, completion: )

            // change to desired number of seconds (in this case 5 seconds)
            let when = DispatchTime.now() + 1
            DispatchQueue.main.asyncAfter(deadline: when)
                // your code with delay
                alert.dismiss(animated: true, completion: nil)
                topWindow.isHidden = true
            
        

        // Show alert view with call back
        static func showAlertWithCB(title: String, message: String, isConditional: Bool, viewController: UIViewController, completionBlock: @escaping (_: Bool) -> Void) 

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

            // Check whether it's conditional or not ('YES' 'NO, or just 'OK')
            if isConditional
            
                alert.addAction(UIAlertAction(title: NSLocalizedString("yes", comment: ""), style: UIAlertActionStyle.default, handler:  (action: UIAlertAction) in
                    alert.dismiss(animated: true, completion: nil)
                    completionBlock(true)
                ))

                alert.addAction(UIAlertAction(title: NSLocalizedString("no", comment: ""), style: UIAlertActionStyle.default, handler:  (action: UIAlertAction) in
                    alert.dismiss(animated: true, completion: nil)
                    completionBlock(false)
                ))
            
            else
            
                alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "ok"), style: UIAlertActionStyle.default, handler:  (action: UIAlertAction) in
                    alert.dismiss(animated: true, completion: nil)
                    completionBlock(true)
                ))
            

            viewController.present(alert, animated: true, completion: nil)
        

        static func showAlert(title: String!, message : String!) -> Void 
            //let appDelegate = UIApplication.shared.delegate as! AppDelegate

            // the alert view
            let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)

            let topWindow = UIWindow(frame: UIScreen.main.bounds)
            topWindow.rootViewController = UIViewController()
            topWindow.windowLevel = UIWindowLevelAlert + 1
            topWindow.makeKeyAndVisible()
            topWindow.rootViewController?.present(alert, animated: true, completion: )

            alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "ok"), style: UIAlertActionStyle.cancel, handler: (_ action: UIAlertAction) -> Void in
                // continue your work
                // important to hide the window after work completed.
                // this also keeps a reference to the window until the action is invoked.
                topWindow.isHidden = true
            ))

        

        static func showComingSoon(viewController: UIViewController) 
            let alert = UIAlertController(title: "", message: "Coming Soon", preferredStyle: .alert)

            viewController.present(alert, animated: true, completion: )

            // change to desired number of seconds (in this case 1 seconds)
            let when = DispatchTime.now() + 0.6
            DispatchQueue.main.asyncAfter(deadline: when)
                // your code with delay
                alert.dismiss(animated: true, completion: nil)
            
        

        static func showGenericErrorMessageAlert(viewController: UIViewController) 
            let alert = UIAlertController(title: NSLocalizedString("error", comment: ""), message: NSLocalizedString("generic.error.message", comment: "") ,preferredStyle: UIAlertControllerStyle.alert)

            alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "ok"), style: UIAlertActionStyle.cancel, handler: nil))

            viewController.present(alert, animated: true, completion: nil)
        

 static func showAlertWithTextField(viewController : UIViewController,completionBlock: @escaping (_: Bool, String) -> Void) 

        //1. Create the alert controller.
        let alert = UIAlertController(title: "Report Event?", message: "", preferredStyle: .alert)
        alert.view.tintColor = APP_ORANGE_COLOR
        //2. Add the text field. You can configure it however you need.
        //AlertUtility.addte
        alert.addTextField  (textField) in

            let heightConstraint = NSLayoutConstraint(item: textField, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50)
            textField.addConstraint(heightConstraint)
            textField.placeholder = "Enter report reason here"
            textField.tintColor = APP_ORANGE_COLOR
            textField.autocapitalizationType = .sentences
        

        // 3. Grab the value from the text field, and print it when the user clicks OK.
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler:  (_) in
            // Force unwrapping because we know it exists.
            completionBlock(true,"")
            //print("Text field: \(textField.text)")
        ))

        // 3. Grab the value from the text field, and print it when the user clicks OK.
        alert.addAction(UIAlertAction(title: "Submit", style: .default, handler:  [weak alert] (_) in
            let textField = alert?.textFields![0] // Force unwrapping because we know it exists.
            completionBlock(true,(textField?.text)!)
            //print("Text field: \(textField.text)")
        ))

        // 4. Present the alert.
        viewController.present(alert, animated: true, completion: nil)

        let textField = alert.textFields![0]
        let v = UIView.init(frame: textField.frame)
        textField.addSubview(v)
        v.frame = textField.frame
        v.bounds = textField.bounds
        v.backgroundColor = APP_ORANGE_COLOR
        v.superview?.bringSubview(toFront: v)
       

    

【讨论】:

以上是关于如何从 Appdelegate 显示 UIAlertController的主要内容,如果未能解决你的问题,请参考以下文章

IOS 从 AppDelegate 中的 addMessageFromRemoteNotification 显示 UIViewController

如何从 appDelegate 呈现 UIAlertView

如何将数据库从 AppDelegate 传递到 ViewController

如何在我的 AppDelegate 上设置多个本地通知?

从 AppDelegate 访问当前视图

如何在 iOS 中从 AppDelegate 调用视图控制器