如果已经显示警报,则显示 UIAlertController

Posted

技术标签:

【中文标题】如果已经显示警报,则显示 UIAlertController【英文标题】:Show UIAlertController if already showing an Alert 【发布时间】:2014-10-17 06:53:30 【问题描述】:

旧版UIAlertView 和新版UIAlertController 的区别在于后者需要使用presentViewController:animated:completion: 呈现到特定的视图控制器上。这给我的用例带来了一个尴尬的问题:如果出现第二个视图控制器(例如由于网络连接失败而导致的错误对话框)时已经显示了UIAlertController(例如评级对话框)怎么办。我经历过,在这种情况下,第二个 UIAlertController 只是没有显示。

编辑:目前我尝试显示警报,我不知道当前是否有任何显示。

你如何应对这种情况?

【问题讨论】:

[本帖][1]正确解释如何解决类似问题[1]:***.com/questions/21179922/… 其他线程中的解决方案脆弱丑陋,在ios8中可能会崩溃。 【参考方案1】:

我找到了一种解决方法来找出我可以在哪个视图控制器上显示警报。我也贴出答案here:

@implementation UIViewController (visibleViewController)

- (UIViewController *)my_visibleViewController 

    if ([self isKindOfClass:[UINavigationController class]]) 
        // do not use method visibleViewController as the presentedViewController could beingDismissed
        return [[(UINavigationController *)self topViewController] my_visibleViewController];
    

    if ([self isKindOfClass:[UITabBarController class]]) 
        return [[(UITabBarController *)self selectedViewController] my_visibleViewController];
    

    if (self.presentedViewController == nil || self.presentedViewController.isBeingDismissed) 
        return self;
    

    return [self.presentedViewController my_visibleViewController];


@end

// To show a UIAlertController, present on the following viewcontroller:
UIViewController *visibleViewController = [[UIApplication sharedApplication].delegate.window.rootViewController my_visibleViewController];

【讨论】:

【参考方案2】:

由于UIAlertController 本身就是UIViewController,因此您可以在第一个UIAlertController 的基础上通过现有的呈现来呈现第二个UIAlertController

alertController.PresentViewController(alertController2,  animated: true, completionHandler: null)

【讨论】:

问题是必须先找出最上面的viewcontroller。假设我不知道是否显示了 amy 警报或其他显示的视图控制器。 使用这个来获取***ViewController: private UIViewController GetTopPresentedViewController() UIViewController currentVC = this.viewController; while (true) UIViewController nextVC = currentVC.PresentedViewController; if (nextVC == null) return currentVC; currentVC = nextVC; 查看我的答案 - 不要忘记 isBeingDismissed 标志。【参考方案3】:

当应用程序必须在窗口上显示一些警报时,此代码满足要求,并且在显示之前检查是否已经显示了任何其他 AlertController,如果已显示,则在出现的 Alertcontroller 上显示警报,否则在窗口上显示。

这里还有另一种选择,您可以根据需要对其进行优化。

     func showAlert(message:String) 

        if let alert = self.checkIfAlertViewHasPresented() 
            alert.presentViewController(alertController, animated: true, completion: nil)

         else 
            self.window?.rootViewController!.presentViewController(alertController, animated: true, completion: nil)
        
    

    func checkIfAlertViewHasPresented() -> UIAlertController? 

        if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController 
            while let presentedViewController = topController.presentedViewController 
                topController = presentedViewController
            
            if topController is UIAlertController 
               return (topController as! UIAlertController)
             else 
               return nil
            
        
        return nil
    

【讨论】:

这不包括接受的答案中涵盖的一些特殊情况。 @fabb 同意你的看法。我在编写代码之前已经说明了该代码块满足的条件。如果一些新手开发人员在理解其他复杂情况时遇到问题,我认为我的答案是次要选择。 恕我直言,尤其是新手应该被教导正确做事,不要冒犯 @fabb 同意你的看法。我会在以后的答案中关注您的建议,稍后我将使用更优化的答案编辑我的答案。 :)【参考方案4】:

这就是我正在使用的。这样,如果警报已经显示, 我更喜欢用户关闭它而不是应用程序。 因此,如果视图已经显示警报,我只需等待 5 秒再试一次。

我只是想补充一下,我没有测试太多,但它有效。(从我做的1个测试中),所以我希望我没有遗漏什么,因为我考虑了这个问题很长时间,并且这个解决方案听起来太简单了:)

-(void) alertUserWithTitle:(NSString*) title Message:(NSString*) message

    UIAlertController* alert = [UIAlertController alertControllerWithTitle:title                                                                              message:message
                                                                            preferredStyle:UIAlertControllerStyleAlert];

                    UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
                                                                          handler:^(UIAlertAction * action) ];

                    [alert addAction:defaultAction];

                    if(self.presentedViewController == nil)
                    
                        [self presentViewController:alert animated:YES completion:nil];
                    else
                    
                        double delayInSeconds = 2.0;
                        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);

                        dispatch_after(popTime, dispatch_get_main_queue(), ^(void)

                            [self alertUserWithTitle:title Message:message];

                        );

                    

【讨论】:

适用于某些用例。如果警报应该阻止进一步的用户交互,那就不太理想了。【参考方案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)
    

【讨论】:

以上是关于如果已经显示警报,则显示 UIAlertController的主要内容,如果未能解决你的问题,请参考以下文章

如果 UITableView 没有条目,则显示警报

如果在 UITextField 中输入了更多字符,则显示警报

如果用户名/电子邮件与特定域不匹配,则显示窗口警报

可以从背景显示的 AlertView

根据值和会话显示警报

当视频文件存在并显示警报时,WebView 显示黑屏