2011-03-22 15 views
21

Tengo una clase de Utils que muestra UIAlertView cuando se activan ciertas notificaciones. ¿Hay alguna manera de descartar cualquier UIAlertViews abierto antes de mostrar uno nuevo?iOS descartar UIAlertView por mostrar otro

CurrentY estoy haciendo esto cuando la aplicación entra en el fondo usando

[self checkViews:application.windows]; 

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

Esto hace que sea fácil en applicationDidEnterBackground que puedo utilizar application.windows

Puede Uso AppDelegate o algo similar para obtener todas las vistas, recorrerlas y descartar cualquier UIAlertViews?

Respuesta

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

Funciona muy bien. Gracias :) –

+2

Parece que esto no funciona en iOS6. – erkanyildiz

+3

... y es por eso que iterar estructuras privadas de vista interna es un proceso frágil. –

27

iOS6 versión compatible:

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

funciona bien, ahorra tiempo ...gracias – Dhaval

+0

Esta debería ser la respuesta aceptada –

+3

Esta es una vieja pregunta, pero esto no está funcionando en iOS 7. – Moy

1

iOS7 versión compatible:

Hice una interfaz categoría que almacena todas las instancias en el método init.

Sé que es una forma muy ineficiente.

#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 

Inicie la supervisión de instancias antes de usar UIAlertView.

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

    [UIAlertView startInstanceMonitor]; 

    return YES; 
} 

Desconexión de llamadasTodo antes de mostrar otro.

[UIAlertView dismissAll]; 

Es mejor utilizar un patrón singleton si puede controlar todas las UIAlertViews.

Pero en mi caso, necesito este código para cerrar el diálogo de alerta de JavaScript en un UIWebView.

0

Desde UIAlertView está en desuso en iOS8 a favor de UIAlertController (que es un UIViewController, presentado de forma modal), no se puede preestablecer 2 alertas al mismo tiempo (de la misma viewController por lo menos). La segunda alerta simplemente no será presentada.

Quería emular parcialmente el comportamiento de UIAlertView, así como evitar mostrar varias alertas a la vez. Bellow es mi solución, que usa rootViewController de window para presentar alertas (generalmente, es el controlador de navegación de appDelegate). Declaré esto en AppDelegate, pero puedes ponerlo donde lo desees.

Si encuentra algún tipo de problema al usarlo, por favor informe aquí en los comentarios.

@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 
Cuestiones relacionadas