开发者

Check if a UIAlertView is showing

I have a method that posts HTTP data and displays a UIAlertView if there is an error. If I have multiple HTTP post I will show multiple UIAlertView for every error.

I want to show a UIAlertVi开发者_开发技巧ew only if is not showing other UIAlertView. How can I determine this?


Why not just check the visible property, maintained by the UIAlertView class?

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


On the object that calls set an ivar before invoking the show method on your UIAlertView.

...

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

...

Then in your delegate method for the alert manage setting your flag ivar to no:

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
  ...
      self.alertShowing = NO;
}

If you want the alerts to show sequentially, I would post notifications to add each message to a queue and then only take a message off the queue after an alert is dismissed.


If you can control the other alert views, check the visible property for each of them.


In iOS 6 or before, when an alert appears, it will be moved to a _UIAlertOverlayWindow. Therefore, a pretty fragile method is to iterate through all windows and check if there's any UIAlertView subviews.

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;

This is undocumented as it depends on internal view hierarchy, although Apple cannot complain about this. A more reliable but even more undocumented method is to check if [_UIAlertManager visibleAlert] is nil.

These methods can't check if a UIAlertView from SpringBoard is shown.


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


Another option that works across the entire app and doesn't involve walking the view stack is to subclass UIAlertView to MyUIAlertView, add a static (class) variable BOOL alertIsShowing, and override the -(void)show selector.

In your overridden show selector, check the alertIsShowing variable. If it's YES then try again after a delay (use dispatch_after or set an NSTimer). If it's NO, go ahead and call [super show] and assign YES to alertIsShowing; when the alert view is hidden, set alertIsShowing back to NO (you'll need to be clever about handling the delegate).

Finally, go through and replace all UIAlertView instances with MyUIAlertView.


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


I think it will work:

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


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


+ (BOOL)checkAlertExist {

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


Some notes on my quest to find the UIAlertView in the view hierarchy:

I tried to loop through all of the [UIApplication sharedApplication].windows view's recursively but couldn't find anything.

The windows property of UIApplication docs states the following:

This property contains the UIWindow objects currently associated with the app. This list does not include windows created and managed by the system, such as the window used to display the status bar.

So this made me realize that the UIWindow where UIAlertView could be located is not even presented to us.

HOWEVER, there is also a property on UIApplication called keyWindow. Upon looping through that, I found private classes that would compose an alert view:

On iOS 7: _UIModalItemHostingWindow, _UIModalItemAlertContentView, _UIBackdropEffectView etc.

On iOS 8: _UIAlertControllerActionView, _UIAlertControllerShadowedScrollView, _UIBackdropView etc.

I could not find the UIAlertView that I presented, but rather, a bunch of classes that compose it internally. So to answer the original question, you can probably use the keyWindow property and see if you notice these classes, but your app could get rejected for trying to check for private classes.

For folks using, the newer, UIAlertController available for iOS 8 could get the reference to it using: [UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜