问题
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 UIAlertController
s, 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