检查 UIAlertController 是否已经显示的最佳方法是什么?

我有一个表格视图,当加载时,每个单元格可能返回一个 NSERror,我已经选择在 UIAlertController 中显示它。问题是,如果返回多个错误,我会在控制台中得到这个错误。

Warning: Attempt to present UIAlertController: 0x14e64cb00 on MessagesMasterVC: 0x14e53d800 which is already presenting (null)

理想情况下,我希望在我的 UIAlertController 扩展方法中处理这个问题。

class func simpleAlertWithMessage(message: String!) -> UIAlertController {


let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)


alertController.addAction(cancel)
return alertController
}

根据 Matt 的回答,我将扩展更改为 UIViewController 扩展,它更加简洁,并且节省了大量 presViewController 代码。

    func showSimpleAlertWithMessage(message: String!) {


let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)


alertController.addAction(cancel)


if self.presentedViewController == nil {
self.presentViewController(alertController, animated: true, completion: nil)
}
}
65853 次浏览

It is not the UIAlertController that is "already presenting", it is MessagesMasterVC. A view controller can only present one other view controller at a time. Hence the error message.

换句话说,如果您已经告诉一个视图控制器 presentViewController:...,那么您不能再这样做,直到所提供的视图控制器被解散。

您可以通过检查 MessagesMasterVC 的 presentedViewController来询问它是否已经显示了视图控制器。如果不是 nil,不要告诉它 presentViewController:...-它已经呈现了一个视图控制器。

if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {


// UIAlertController is presenting.Here


}

好吧,从我的角度来看,以上建议的解决方案存在一个根本问题:

如果您询问 ViewController,属性‘ presedViewController’是否为 null,答案是否为 false,那么您不能得出结论,您的 UIAlertController 已经出现了。它可以是任何显示的 ViewController,例如 popOver。因此,我建议检查一下 Alert 是否已经显示在屏幕上(将现有的 ViewController 转换为 UIAlertController) :

if self.presentedViewController == nil {
// do your presentation of the UIAlertController
// ...
} else {
// either the Alert is already presented, or any other view controller
// is active (e.g. a PopOver)
// ...


let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?


if thePresentedVC != nil {
if let thePresentedVCAsAlertController : UIAlertController = thePresentedVC as? UIAlertController {
// nothing to do , AlertController already active
// ...
print("Alert not necessary, already on the screen !")


} else {
// there is another ViewController presented
// but it is not an UIAlertController, so do
// your UIAlertController-Presentation with
// this (presented) ViewController
// ...
thePresentedVC!.presentViewController(...)


print("Alert comes up via another presented VC, e.g. a PopOver")
}
}

}

This category can auto-manage all the modal controllers include of UIAlertController.

UIViewController + JCPresentQueue.h

只需取消当前控制器并显示所需的控制器即可 也就是说。

self.dismiss(animated: false, completion: nil)

self.displayAlertController()

这是我在 Swift 3中使用的一个解决方案。它是一个向用户显示警报的函数,如果在用户解除警报之前多次调用它,它将向已经显示的警报添加新的警报文本。如果呈现其他视图,则不会显示警报。并非所有人都同意这种行为,但它在简单的情况下很有效。

extension UIViewController {
func showAlert(_ msg: String, title: String = "") {
if let currentAlert = self.presentedViewController as? UIAlertController {
currentAlert.message = (currentAlert.message ?? "") + "\n\nUpdate:\(title): \(msg)"
return
}


// create the alert
let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))


// show the alert
self.present(alert, animated: true, completion: nil)
}
}

我们可以简单地检查是否存在任何视图控制器。

if presented then check if it is kind of UIAlertController .

    id alert = self.presentedViewController;


if (alert && [alert isKindOfClass:[UIAlertController class]])
{
*// YES UIAlertController is already presented*
}
else
{
// UIAlertController is not presented OR visible.
}

我用它来检测,移除和警报。

首先,我们使用以下函数创建一个警报。

 var yourAlert :UIAlertController!


func useYouAlert (header: String, info:String){




yourAlert = UIAlertController(title:header as String, message: info as String, preferredStyle: UIAlertControllerStyle.alert)






let okAction = UIAlertAction(title: self.langText[62]as String, style: UIAlertActionStyle.default) { (result : UIAlertAction) -> Void in
print("OK")


}




yourAlert.addAction(okAction)
self.present(yourAlert.addAction, animated: true, completion: nil)


}

还有你代码的其他部分

    if yourAlert != nil {


yourAlert.dismiss(animated: true, completion: nil)


}

你可以使用以下最新的 Swift 语言:

var alert = presentedViewController


if alert != nil && (alert is UIAlertController) {
// YES UIAlertController is already presented*
} else {
// UIAlertController is not presented OR visible.
}

Swift 4.2+ Answer

if UIApplication.topViewController()!.isKind(of: UIAlertController.self) {
print("UIAlertController is presented")}

对于那些谁不知道如何获得顶部大多数视图控制器

extension UIApplication {




public class func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}}

快速5 + 答案 “ keyWindow” 在 iOS 13.0中已被弃用 建议编辑

if UIApplication.topViewController()!.isKind(of: UIAlertController.self) {
print("UIAlertController is presented")}

For those who don't know how to get top most Viewcontroller

extension UIApplication {




public class func topViewController(_ base: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}}

取消当前控制器并显示警报控制器,如

 func alert(_ message:String) {
let alert = UIAlertController(title: "Error!", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
self.dismiss(animated: false, completion: nil)
self.present(alert, animated: true,completion: nil)
}

you can test - in a single line - if an alert is already presented:

if self.presentedViewController as? UIAlertController != nil {
print ("alert already presented")
}

I found I needed to create a queue to stack the UIAlertController requests.

NSMutableArray *errorMessagesToShow; // in @interface
errorMessagesToShow=[[NSMutableArray alloc] init];  // in init


-(void)showError:(NSString *)theErrorMessage{
if(theErrorMessage.length>0){
[errorMessagesToShow addObject:theErrorMessage];
[self showError1];
}
}
-(void)showError1{
NSString *theErrorMessage;
if([errorMessagesToShow count]==0)return; // queue finished


UIViewController* parentController =[[UIApplication sharedApplication]keyWindow].rootViewController;
while( parentController.presentedViewController &&
parentController != parentController.presentedViewController ){
parentController = parentController.presentedViewController;
}
if([parentController isKindOfClass:[UIAlertController class]])return;  // busy


// construct the alert using [errorMessagesToShow objectAtIndex:0]
//  add to each UIAlertAction completionHandler [self showError1];
//   then


[errorMessagesToShow removeObjectAtIndex:0];
[parentController presentViewController:alert animated:YES completion:nil];
}