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

8
传统的UIAlertView和新的UIAlertController之间的区别在于,后者需要使用presentViewController:animated:completion:方法展示到特定的视图控制器上。这给我的使用情况带来了一些尴尬的问题:如果已经有一个UIAlertController显示(例如评级对话框),当第二个视图控制器被呈现时(例如由于网络连接失败而出现的错误对话框),该怎么办?我发现在这种情况下,第二个UIAlertController就不会显示。

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

您如何解决这种情况?


这个帖子正确地解释了如何解决类似的问题。 - user4034301
另一个线程中的解决方案很脆弱且难看,很可能在iOS8中会出现问题。 - fabb
5个回答

5

由于 UIAlertController 本身就是一个 UIViewController,因此可以通过从现有的控制器中呈现另一个 UIAlertController 来在第一个上面呈现第二个:

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

问题在于首先必须找到顶部呈现的视图控制器。假设我不知道是否有任何警报或其他呈现的视图控制器正在显示。 - fabb
使用此方法获取顶层视图控制器: private UIViewController GetTopPresentedViewController() { UIViewController currentVC = this.viewController; while (true) { UIViewController nextVC = currentVC.PresentedViewController; if (nextVC == null) { return currentVC; } currentVC = nextVC; } } - Bbx
请注意我的回答 - 不要忘记 isBeingDismissed 标志。 - fabb

5

我找到了一个解决办法来确定我可以在哪个视图控制器上显示警报。我还在这里发布了答案:

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

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

0

这段代码实现了一个功能,当应用程序需要在窗口上显示一些警报时,在呈现之前会检查是否已经有其他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 我同意你的观点。在编写代码之前,我已经说明了代码块满足的条件。如果一些新手开发者难以理解其他复杂条件,我认为我的答案是一个次要选项。 - iDevAmit
在我看来,特别是新手应该被教导如何正确地做事,无意冒犯。 - fabb
@fabb 我同意你的意见。我会在我的未来答案中考虑到你的建议,稍后我会编辑我的答案,提供更优化的解决方案。 :) - iDevAmit

0

这是我正在使用的方法。如果警报已经显示,我更喜欢用户自己解除它而不是应用程序。 因此,如果视图已经呈现警报,我只需等待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];

                        });

                    }
}

适用于某些用例。如果警报应该阻止进一步的用户交互,则不太理想。 - fabb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接