检查一个UIAlertView是否正在显示

42

我有一个方法用于发布HTTP数据,并在出现错误时显示UIAlertView。如果我进行多个HTTP post,每次出现错误时都会显示多个UIAlertView。

我希望只有没有其他UIAlertView正在显示时才显示UIAlertView。如何确定这一点?

10个回答

60

为什么不直接检查UIAlertView类维护的visible属性呢?

if (_alert) //alert is a retained property
{
    self.alert = [[[UIAlertView alloc] initWithTitle:@"Your Title"
                                             message:@"Your message" 
                                            delegate:self
                                   cancelButtonTitle:@"Cancel"
                                   otherButtonTitles:@"OK"] autorelease];
}
if (!_alert.visible)
{
    [_alert show];
}

4
自从iOS9开始,替换的 'UIAlertController' 不再具有 'visible' 属性。 - KoreanXcodeWorker

52

在调用UIAlertView的show方法之前,在调用它的对象中设置ivar。

...

if (!self.alertShowing) {
    theAlert = [[UIAlertView alloc] initWithTitle:title message:details delegate:self cancelButtonTitle:nil otherButtonTitles:@"Okay", nil];
    self.alertShowing = YES;
    [theAlert show];
}

...

然后在你的警告视图的委托方法中,将你的标记实例变量设为否:

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
  ...
      self.alertShowing = NO;
}
如果您希望警报按顺序显示,我建议将通知发布到队列中以添加每条消息,然后只有在警报被取消后才从队列中取出一条消息。

2
如果弹出框显示,但它可能属于其他视图控制器,而您不知道每个视图控制器是什么怎么办? - Gargo
11
不需要一个ivar,只需检查visible属性,就像下面的答案一样。 - sam-w
我检查了警告视图的isFirstResponder属性,结果为true,这也起作用了。 - Saran
当最初提出问题时,visible属性并未公开。如果现在的答案更正确,请随意投票支持它,但是在原始问题和属性转换(包括visible)之间已经过去了两年。 - Chip Coons
在UIAlertView(现在是UIAlertController)上创建一个类别,该类别将![self.view isHidden]作为alertShowing的值返回。如果您给警报视图设置了标记,并将其定义为枚举,则可以通过标记的枚举名称检查该视图。 - Alex Zavatone
显示剩余2条评论

26

如果你可以控制其他警示框,检查每个警示框的visible属性。


在iOS 6或之前,当一个警示框出现时,它将被移动到一个名为_UIAlertOverlayWindow的窗口上。因此,一种比较脆弱的方法是遍历所有窗口,检查是否有任何UIAlertView子视图。

for (UIWindow* window in [UIApplication sharedApplication].windows) {
  NSArray* subviews = window.subviews;
  if ([subviews count] > 0)
    if ([[subviews objectAtIndex:0] isKindOfClass:[UIAlertView class]])
      return YES;
}
return NO;

这是未记录在案的,因为它取决于内部视图层次结构,尽管苹果不会抱怨此事。更可靠但更不受记录的方法是检查[_UIAlertManager visibleAlert]是否为空

这些方法无法检查SpringBoard中是否显示了UIAlertView。


1
你的代码中有一个错别字:“UIView* subviews = window.subviews;”,应该改为“NSArray* subviews = window.subviews;”。顺便说一下,这对我帮助很大,谢谢。 - Rahul Vyas
@Deepesh,你找到解决方案了吗? - LiangWang
@Jacky,我还没有找到正确的解决方案...如果你找到了任何解决方案...请粘贴你宝贵的答案...谢谢。 - Deepesh

9
- (BOOL)checkAlertExist {
    for (UIWindow* window in [UIApplication sharedApplication].windows) {
        NSArray* subviews = window.subviews;
        if ([subviews count] > 0) {
            for (id cc in subviews) {
                if ([cc isKindOfClass:[UIAlertView class]]) {
                    return YES;
                }
            }
        }
    }
    return NO;
}

1
这在iOS 7中不起作用。第一个子视图可以是容器。因此,您必须遍历整个可能的树。 - Womble
@Womble,你找到解决方案了吗? - LiangWang

4

另一种在整个应用程序中都有效且不涉及遍历视图堆栈的选项是将UIAlertView子类化为MyUIAlertView,添加静态(类)变量BOOL alertIsShowing并重写-(void)show选择器。

在您重写的show选择器中,检查alertIsShowing变量。如果它是YES,那么延迟一段时间后再尝试(使用dispatch_after或设置NSTimer)。如果是NO,那么继续调用[super show]并分配YESalertIsShowing;当警报视图被隐藏时,将alertIsShowing设置回NO(您需要聪明地处理委托)。

最后,逐个替换所有UIAlertView实例为MyUIAlertView


1
我喜欢这个答案(尽管我子类化了 UIAlertController - 如果你同时使用两者,我认为你需要跟踪两者)。最终我使用了一个计数器而不是布尔值。这样,如果您尝试同时显示两个警报,则不会在出现/消失方法与您的预期不同步时意外将布尔值设置为 false。 - garie

3

Swift:

func showAlert(withTitle title: String, message: String, viewController: UIViewController) {
    if viewController.presentedViewController == nil { // Prevent multiple alerts at the same time
        let localizedTitle = NSLocalizedString(title, comment: "")
        let localizedMessage = NSLocalizedString(message, comment: "")
        let alert = UIAlertController(title: localizedTitle, message: localizedMessage, preferredStyle: .Alert)
        let action = UIAlertAction(title: "OK", style: .Default, handler: nil)
        alert.addAction(action)

        viewController.presentViewController(alert, animated: true, completion: nil)
    }
}

viewntroller.presentedViewController == nil 这招起作用了。谢谢 - Kingalione

3

我认为这会奏效:

-(BOOL) doesAlertViewExist {
    if ([[UIApplication sharedApplication].keyWindow isMemberOfClass:[UIWindow class]])
    {
        return NO;//AlertView does not exist on current window
    }
    return YES;//AlertView exist on current window
}

更少的代码 -(BOOL) doesAlertViewExist { return ![[UIApplication sharedApplication].keyWindow isMemberOfClass:[UIWindow class]]; } - Popmedic

2
// initialize default flag for alert... If alert is not open set isOpenAlert as NO
BOOL isAlertOpen;
isAlertOpen = NO;
if (isAlertOpen == NO) {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:@"Alert is Open" delegate:self cancelButtonTitle:@"Okay!!" otherButtonTitles: nil];
    [alert show];
    // Now set isAlertOpen to YES
    isAlertOpen = YES;
}
else
{
    //Do something
}

2
+ (BOOL)checkAlertExist {

    for (UIWindow* window in [UIApplication sharedApplication].windows) {
        if ([window.rootViewController.presentedViewController isKindOfClass:[UIAlertController class]]) {
            return YES;
        }
    }
    return NO;
}

1

在寻找视图层次结构中的UIAlertView时,我有一些笔记:

我试图递归遍历所有[UIApplication sharedApplication].windows视图,但是找不到任何东西。

UIApplication文档中的windows属性说明如下:

此属性包含当前与应用程序关联的UIWindow对象。 此列表不包括由系统创建和管理的窗口,例如用于显示状态栏的窗口。

这使我意识到,UIAlertView可能位于的UIWindow甚至没有呈现给我们。

然而,UIApplication还有一个名为keyWindow的属性。 在循环遍历该属性时,我发现了组成警报视图的私有类:

iOS 7上:_UIModalItemHostingWindow_UIModalItemAlertContentView_UIBackdropEffectView等。

iOS 8上:_UIAlertControllerActionView_UIAlertControllerShadowedScrollView_UIBackdropView等。

我找不到我所呈现的UIAlertView,但是实际上,有一堆内部组成它的类。因此回答最初的问题,你可能可以使用keyWindow属性并查看是否注意到这些类,但是尝试检查私有类可能会导致你的应用被拒绝。
对于使用新版iOS 8的人们,可以使用[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController来获取对其的引用。

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