Dismiss all UIAlertControllers currently presented

纵然是瞬间 提交于 2019-12-23 20:44:51

问题


Is there a way to dismiss all UIAlertControllers that are currently presented?

This is specifically because from anywhere and any state of my app, I need to get to a certain ViewController when a push notification is pressed.


回答1:


You could subclass your UIAlertControllers, attach NSNotification observers to each which would trigger a method within the UIAlertController subclass to dismiss the alert controller, then post an NSNotification whenever you're ready to dismiss, ex:

class ViewController: UIViewController {
    func presentAlert() {
        // Create alert using AlertController subclass
        let alert = AlertController(title: nil, message: "Message.", preferredStyle: UIAlertControllerStyle.Alert)
        // Add observer to the alert
        NSNotificationCenter.defaultCenter().addObserver(alert, selector: Selector("hideAlertController"), name: "DismissAllAlertsNotification", object: nil)
        // Present the alert
        self.presentViewController(alert, animated: true, completion:nil)
    }
}

// AlertController subclass with method to dismiss alert controller
class AlertController: UIAlertController {
    func hideAlertController() {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}

Then post the notification whenever you're ready to dismiss the alert (in this case, when the push notification is pressed):

NSNotificationCenter.defaultCenter().postNotificationName("DismissAllAlertsNotification", object: nil)



回答2:


func dismissAnyAlertControllerIfPresent() {
    guard let window :UIWindow = UIApplication.shared.keyWindow , var topVC = window.rootViewController?.presentedViewController else {return}
    while topVC.presentedViewController != nil  {
        topVC = topVC.presentedViewController!
    }
    if topVC.isKind(of: UIAlertController.self) {
        topVC.dismiss(animated: false, completion: nil)
    }
}

This worked for me!




回答3:


You can dismiss an UIAlertController that is currently being presented to the user this way:

self.dismissViewControllerAnimated(true, completion: nil)



回答4:


They are modal: there will only be one of them at any time and it will have full focus.

It is your responsibility to create your alerts and it is the user that should be dismissing them. You need to make (or use) a custom view to display a stack of messages if you need further control.




回答5:


I have written more generic code in swift 4, This class shows alerts using utility class.

import UIKit

let APP_ORANGE_COLOR = UIColor(red: 1.000, green: 0.412, blue: 0.000, alpha: 1.00)

extension UIAlertController {
    @objc func hideAlertController() {
        self.dismiss(animated: false, completion: nil)
    }
}

class AlertUtility: UIViewController {

    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))
        alert.view.tintColor = APP_ORANGE_COLOR

        NotificationCenter.default.addObserver(alert, selector: #selector(alert.hideAlertController), name: DismissAllAlertsNotification, object: 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
        }
    }

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

        NotificationCenter.default.addObserver(alert, selector: #selector(alert.hideAlertController), name: DismissAllAlertsNotification, object: nil)
        alert.view.tintColor = APP_ORANGE_COLOR
    }


    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))
        alert.view.tintColor = APP_ORANGE_COLOR
        viewController.present(alert, animated: true, completion: nil)
        NotificationCenter.default.addObserver(alert, selector: #selector(alert.hideAlertController), name: DismissAllAlertsNotification, object: nil)
    }

    static func showComingSoonAlert(viewController: UIViewController) {

        // the alert view
        let alert = UIAlertController(title: "", message: NSLocalizedString("coming.soon", comment: ""), preferredStyle: .alert)

        viewController.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)
        }
        NotificationCenter.default.addObserver(alert, selector: #selector(alert.hideAlertController), name: DismissAllAlertsNotification, object: nil)
    }

    // 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)
        alert.view.tintColor = APP_ORANGE_COLOR
        // Check whether it's conditional or not ('YES' 'NO, or just 'OK')
        if isConditional
        {
            alert.addAction(UIAlertAction(title: NSLocalizedString("cancel", comment: ""), style: UIAlertActionStyle.cancel, handler: { (action: UIAlertAction) in
                alert.dismiss(animated: true, completion: nil)
                completionBlock(false)
            }))

            alert.addAction(UIAlertAction(title: NSLocalizedString("yes", comment: ""), style: UIAlertActionStyle.default, handler: { (action: UIAlertAction) in
                alert.dismiss(animated: true, completion: nil)
                completionBlock(true)
            }))

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

        NotificationCenter.default.addObserver(alert, selector: #selector(alert.hideAlertController), name: DismissAllAlertsNotification, object: 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: { [weak alert] (_) 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)
    }

}

Use it in this way

//sample code - use in your view controller
AlertUtility.showAlertWithCB(title: NSLocalizedString("alert", comment: "") , message: (error)!, isConditional: false, viewController: self, completionBlock: { (yes) in

                      //Your actions on callback
                        self.popToPreviousController()
                    })
AlertUtility.showAlert(title: ALERT_TITLE, message: message, viewController: self)

Post notification when you want/need to auto dismiss alerts in app

let DismissAllAlertsNotification = Notification.Name("DismissAllAlertsNotification")

NotificationCenter.default.post(name: DismissAllAlertsNotification, object: nil)



回答6:


I used the following extension to achieve this, I hope it may help you

First; you will have an extension for UIApplication to retrieve the RootViewController

extension UIApplication {

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

Second; extension for UIAlertViewController

extension UIAlertController {
     static func dismissPresentedAlertViewController() {
         let viewController = UIApplication.topViewControllerInNavigationStack()
         guard let isKindOf = viewController?.isKind(of: 
         UIAlertController.classForCoder()), isKindOf else {
             return
         }
         viewController?.dismiss(animated: false, completion: nil)
    }
}


来源:https://stackoverflow.com/questions/28636640/dismiss-all-uialertcontrollers-currently-presented

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!