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


109

我有一个tableview,在加载时,每个单元都可能返回一个NSError,我选择将其显示在UIAlertController中。问题是如果返回多个错误,我会在控制台中收到此错误。

警告:尝试在已显示的MessagesMasterVC:0x14e53d800上呈现UIAlertController:0x14e64cb00(空)

理想情况下,我希望在我的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扩展名,它更加简洁,并节省了许多presentViewController代码。

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

感谢您发布更新的代码。
djbp

我还将其余代码(三行设置UIAlertController)移到If语句中,因为它仍然给出以下错误(不允许在分配视图时尝试加载视图控制器的视图,这可能会导致未定义的行为)
Kitson

我想在下面的链接上引用该解决方案,请检查 stackoverflow.com/a/39994115/1872233
iDevAmit 2016年

Answers:


119

不是UIAlertController在“已经呈现”,而是MessagesMasterVC。一个视图控制器一次只能显示一个其他视图控制器。因此,错误消息。

换句话说,如果您已将View Controller告知,则您必须presentViewController:...在关闭所提供的View Controller之前再做一次。

您可以通过检查MessagesMasterVC的,询问它是否已经在提供视图控制器presentedViewController。如果不是nil,请不要告诉它presentViewController:...-它已经在提供视图控制器。


2
如果控制器A提出了控制器B,然后B提出了UIAlertController,那行得通吗?我遇到了相同的错误,我无法确定B是否已经呈现了我不知道的东西,或者问题是否是因为B正在由A呈现
Christopher Francisco

1
@ChristopherFrancisco提出一个新问题!
马特2015年

@ChristopherFrancisco嗨,我现在有同样的问题,您是否对此提出了新问题?还是在哪里可以解决?如果是,如何?
Abed Naseri

好的答案,那是一个微妙的区别。
ScottyBlades

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

      // UIAlertController is presenting.Here

}

22
在答案中添加一些文字来解释您的工作始终是一个好主意。阅读如何写一个好的答案
耶尔- [R

1
由于缺乏解释,所以不是一个很好的答案,但是该方法对我有很大帮助-问题是我有多个事件调用我的代码来UIAlertController连续短暂地触发。如果您有类似的问题,请检查此。
ChidG'6

10

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

如果您问ViewController,“ presentedViewController”属性是否为nil且答案是否为假,则无法得出UIAlertController已经显示的结论。它可以是任何呈现的ViewController,例如popOver。因此,我的建议是以下检查(是否已在屏幕上显示警报)(将presentedViewController作为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")
      }
  }

}


5

这是我在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)
    }
}

好的,这就是我所需要的。它也适用于iOS 13。
Zoltan Vinkler,

3

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

如果存在,则检查它是否为UIAlertController。

    id alert = self.presentedViewController;

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

1

您可以单行测试-是否已显示警报:

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

您可以在答案中解释代码。或者有如何增加相关信息时,已经是一个公认的或高评级复读如何写一个很好的答案
LEA皮诺


0

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

首先,我们创建具有以下功能的警报。

 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)

    }

0

对于最新的Swift语言,您可以使用以下代码:

var alert = presentedViewController

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

0

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

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

0

Swift 4.2+答案

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

对于那些不知道如何获得最高Viewcontroller的人

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

在iOS 13.0中不推荐使用 Swift 5+ Answer'keyWindow' 建议编辑

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

对于那些不知道如何获得最高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
}}

0

我发现我需要创建一个队列来堆叠UIAlertController请求。

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]; 
}

-3

只需关闭当前控制器,然后显示您想要的控制器即可,即

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

self.displayAlertController()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.