22

我有一个 Utils 类,它在触发某些通知时显示 UIAlertView。有没有办法在显示新的 UIAlertViews 之前关闭任何打开的 UIAlertViews?

当前,当应用程序使用进入后台时,我正在执行此操作

[self checkViews:application.windows];

在 applicationDidEnterBackground

- (void)checkViews:(NSArray *)subviews {
    Class AVClass = [UIAlertView class];
    Class ASClass = [UIActionSheet class];
    for (UIView * subview in subviews){
        if ([subview isKindOfClass:AVClass]){
            [(UIAlertView *)subview dismissWithClickedButtonIndex:[(UIAlertView *)subview cancelButtonIndex] animated:NO];
        } else if ([subview isKindOfClass:ASClass]){
            [(UIActionSheet *)subview dismissWithClickedButtonIndex:[(UIActionSheet *)subview cancelButtonIndex] animated:NO];
        } else {
            [self checkViews:subview.subviews];
        }
    }
}

这使得 applicationDidEnterBackground 变得容易,因为我可以使用 application.windows

我可以使用 AppDelegate 或任何类似的东西来获取所有视图,遍历它们并关闭任何 UIAlertViews 吗?

4

4 回答 4

28
for (UIWindow* window in [UIApplication sharedApplication].windows) {
  NSArray* subviews = window.subviews;
  if ([subviews count] > 0)
    if ([[subviews objectAtIndex:0] isKindOfClass:[UIAlertView class]])
      [(UIAlertView *)[subviews objectAtIndex:0] dismissWithClickedButtonIndex:[(UIAlertView *)[subviews objectAtIndex:0] cancelButtonIndex] animated:NO];
}
于 2011-03-23T02:57:46.327 回答
27

iOS6兼容版本:

for (UIWindow* w in UIApplication.sharedApplication.windows)
    for (NSObject* o in w.subviews)
        if ([o isKindOfClass:UIAlertView.class])
            [(UIAlertView*)o dismissWithClickedButtonIndex:[(UIAlertView*)o cancelButtonIndex] animated:YES];
于 2012-09-23T18:52:59.123 回答
0

iOS7兼容版本:

我制作了一个将所有实例存储在 init 方法中的类别接口。

我知道这是一种非常低效的方式。

#import <objc/runtime.h>
#import <objc/message.h>

@interface UIAlertView(EnumView)

+ (void)startInstanceMonitor;
+ (void)stopInstanceMonitor;
+ (void)dismissAll;
@end

@implementation UIAlertView(EnumView)
static BOOL _isInstanceMonitorStarted = NO;

+ (NSMutableArray *)instances
{
    static NSMutableArray *array = nil;
    if (array == nil)
        array = [NSMutableArray array];

    return array;
}


- (void)_newInit
{
    [[UIAlertView instances] addObject:[NSValue valueWithNonretainedObject:self]];
    [self _oldInit];
}

- (void)_oldInit
{
    // dummy method for storing original init IMP.
}

- (void)_newDealloc
{
    [[UIAlertView instances] removeObject:[NSValue valueWithNonretainedObject:self]];
    [self _oldDealloc];

}
- (void)_oldDealloc
{
    // dummy method for storing original dealloc IMP.
}

static void replaceMethod(Class c, SEL old, SEL new)
{
    Method newMethod = class_getInstanceMethod(c, new);
    class_replaceMethod(c, old, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
}

+ (void)startInstanceMonitor
{
    if (!_isInstanceMonitorStarted) {
        _isInstanceMonitorStarted = YES;
        replaceMethod(UIAlertView.class, @selector(_oldInit), @selector(init));
        replaceMethod(UIAlertView.class, @selector(init), @selector(_newInit));

        replaceMethod(UIAlertView.class, @selector(_oldDealloc), NSSelectorFromString(@"dealloc"));
        replaceMethod(UIAlertView.class, NSSelectorFromString(@"dealloc"), @selector(_newDealloc));
    }
}

+ (void)stopInstanceMonitor
{
    if (_isInstanceMonitorStarted) {
        _isInstanceMonitorStarted = NO;
        replaceMethod(UIAlertView.class, @selector(init), @selector(_oldInit));
        replaceMethod(UIAlertView.class, NSSelectorFromString(@"dealloc"), @selector(_oldDealloc));
    }
}

+ (void)dismissAll
{
    for (NSValue *value in [UIAlertView instances]) {
        UIAlertView *view = [value nonretainedObjectValue];

        if ([view isVisible]) {
            [view dismissWithClickedButtonIndex:view.cancelButtonIndex animated:NO];
        }
    }
}
@end

在使用 UIAlertView 之前启动实例监控。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   //...  
   //...

    [UIAlertView startInstanceMonitor];

    return YES;
}

在显示另一个之前调用dismissAll。

[UIAlertView dismissAll];

如果您可以控制所有 UIAlertView,最好使用单例模式。

但就我而言,我需要这段代码来关闭 UIWebView 中的 javascript 警报对话框。

于 2014-07-15T07:22:45.693 回答
0

由于UIAlertView在 iOS8 中已弃用UIAlertController(这是 a UIViewController,以模态方式呈现),因此您不能同时预设 2 个警报(至少来自同一个 viewController)。根本不会显示第二个警报。

我想部分模仿UIAlertView的行为,并防止一次显示多个警报。Bellow 是我的解决方案,它使用窗口rootViewController来呈现警报(通常是 appDelegate 的导航控制器)。我在 AppDelegate 中声明了这一点,但你可以把它放在你想要的地方。

如果您在使用它时遇到任何问题,请在此处在评论中报告。

@interface UIViewController (UIAlertController)

// these are made class methods, just for shorter semantics. In reality, alertControllers
// will be presented by window's rootViewController (appdelegate.navigationController)
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
                                     message:(NSString *)message
                           cancelButtonTitle:(NSString *)cancelButtonTitle
                           otherButtonTitles:(NSArray *)otherButtonTitles
                                     handler:(void (^)(NSInteger buttonIndex))block;
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
                                     message:(NSString *)message
                           cancelButtonTitle:(NSString *)cancelButtonTitle;

@end

@implementation UIViewController (UIAlertController)

+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
                                     message:(NSString *)message
                           cancelButtonTitle:(NSString *)cancelButtonTitle
{
    return [self presentAlertWithTitle:title message:message cancelButtonTitle:cancelButtonTitle
                     otherButtonTitles:nil handler:nil];
}
+ (UIAlertController *)presentAlertWithTitle:(NSString *)title
                                     message:(NSString *)message
                           cancelButtonTitle:(NSString *)cancelButtonTitle
                           otherButtonTitles:(NSArray *)otherButtonTitles
                                     handler:(void (^)(NSInteger buttonIndex))block
{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message
                                                            preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:cancelButtonTitle style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        if (block)
            block(0);
    }];
    [alert addAction:cancelAction];
    [otherButtonTitles enumerateObjectsUsingBlock:^(NSString *title, NSUInteger idx, BOOL *stop) {
        UIAlertAction *action = [UIAlertAction actionWithTitle:title style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
            if (block)
                block(idx + 1); // 0 is cancel
        }];
        [alert addAction:action];
    }];

    id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
    UIViewController *rootViewController = appDelegate.window.rootViewController;
    if (rootViewController.presentedViewController) {
        [rootViewController dismissViewControllerAnimated:NO completion:^{
            [rootViewController presentViewController:alert animated:YES completion:nil];
        }];
    } else {
        [rootViewController presentViewController:alert animated:YES completion:nil];
    }
    return alert;
}

@end
于 2015-06-20T11:21:46.810 回答