iOS 在显示另一个之前关闭 UIAlertView

Posted

技术标签:

【中文标题】iOS 在显示另一个之前关闭 UIAlertView【英文标题】:iOS dismiss UIAlertView beforing showing another 【发布时间】:2011-07-20 22:20:25 【问题描述】:

我有一个 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 吗?

【问题讨论】:

【参考方案1】:
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];

【讨论】:

……这就是为什么迭代私有内部视图结构是一个脆弱的过程。 对不起,我使用了相同的代码,但它不适合我。 这不再适用于 ios 7。 _UIModalItemHostingWindow中添加了Alertview,并没有对其的引用。 有谁知道如何在 ios 8 或更高版本中完成上述任务。【参考方案2】:

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

【讨论】:

你找到 iOS 7 的替代品了吗?【参考方案3】:

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 警报对话框。

【讨论】:

【参考方案4】:

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

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

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

@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

【讨论】:

以上是关于iOS 在显示另一个之前关闭 UIAlertView的主要内容,如果未能解决你的问题,请参考以下文章

iOS 恢复 - 为啥在我正在恢复的视图之前显示另一个视图?

在所有内容之上显示一个 UIAlertController 并保持在顶部,即使推送另一个视图(iOS 13)

我应该在关闭视图之前关闭键盘吗?

在准备另一个Statement之前是不是需要关闭PreparedStatement

在打开另一个下拉菜单之前完全关闭一个下拉菜单

jQuery 面板 - 在打开另一个 DIV 之前切换一个 DIV 关闭!