¿Descartar un UIAlertView en ios 7 que no funciona?

Estoy intentando descartar un UIAlertView antes de mostrar otro y encontré la respuesta aquí: iOS descarta UIAlertView dándose count de otro

El problema es que esto no funciona en iOS7, pero funciona en iOS6.

Esto funciona en iOS6

-(void)closePreviousAlert{ for (UIWindow* w in [UIApplication shanetworkingApplication].windows) for (NSObject* o in w.subviews) if ([o isKindOfClass:[UIAlertView class]]) [(UIAlertView*)o dismissWithClickedButtonIndex:[(UIAlertView*)o cancelButtonIndex] animated:YES]; } 

¿Hay otra solución para esto?

En lugar de utilizar su enfoque O (n ^ 2) para cerrar la alerta, probablemente sea más ligero (y iOS 7 válido) crear properties privadas para sus alertas y references y descartarlas mediante sus captadores sintetizados. Además, de vez en cuando, establezco una label en la alerta y la reference a través de su label como una solución rápida y sucia.

Si cualquiera de estas soluciones es demasiado simple para el context de su aplicación, podría sugerirle que replantee su uso de las alertas. Demasiadas aplicaciones abusan de las alertas y, en mi opinión, deberían usarse con moderación, solo para agregar algunos comentarios no solicitados :).

Un enfoque diferente que podría ayudarlo es implementar una callback basada en bloque al completar la vida de la alerta. Consulte Simplificar UIAlertView con bloques .

Su código no es válido en iOS7 porque [UIApplication shanetworkingApplication].windows no hace reference a UIAlertView ya que UIAlertView nunca se agrega a ninguna window en iOS7.

Debe seguir haciendo reference a su hoja de acción, esto es lo mejor que puede hacer.

Puede hacer esto con una reference a https://stackoverflow.com/a/19275311/1262634 :

 Class UIAlertManager = NSClassFromString(@"_UIAlertManager"); UIAlertView *alertView = [UIAlertManager performSelector:@selector(topMostAlert)]; 

Editar : esta es una API privada.

Otro método para realizar un seguimiento de las instancias visibles de UIAlertView es mediante el método swizzling:

UIAlertView + Dismiss.h:

 #import <UIKit/UIKit.h> @interface UIAlertView (Dismiss) + (void)dismissAllVisibleAlertViews; @end 

UIAlertView + Dismiss.m:

 #import "UIAlertView+Dismiss.h" #import <objc/runtime.h> // see http://nshipster.com/method-swizzling/ static inline void swizzle(Class class, SEL originalSelector, SEL swizzledSelector) { Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } @implementation UIAlertView (Dismiss) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ swizzle([self class], @selector(show), @selector(xxx_show)); swizzle([self class], @selector(dismissWithClickedButtonIndex:animated:), @selector(xxx_dismissWithClickedButtonIndex:animated:)); }); } + (void)dismissAllVisibleAlertViews { for (NSValue *value in [self visibleAlertViews]) { id val = value.nonretainedObjectValue; if ([val isKindOfClass: [UIAlertView class]]) { [val dismissWithClickedButtonIndex: 0 animated: YES]; } } } #pragma mark - Method Swizzling - (void)xxx_show { [self xxx_show]; [[self.class visibleAlertViews] addObject: [NSValue valueWithNonretainedObject: self]]; } - (void)xxx_dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated { [self xxx_dismissWithClickedButtonIndex: buttonIndex animated: animated]; [[self.class visibleAlertViews] removeObject: [NSValue valueWithNonretainedObject: self]]; } #pragma mark - Cache + (NSMutableSet *)visibleAlertViews { static NSMutableSet *views = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ views = [NSMutableSet new]; }); return views; } @end 

Esto funciona ya que UIAlertViews se muestra llamando al método show . El método swizzled rastrea la instancia hasta que se descarta mediante el dismissWithClickedButtonIndex:animated: A continuación, puede descartar fácilmente todas las vistas de alerta llamando a [UIAlertView dismissAllVisibleAlertViews]; .

Xcode 6.4, para iOS8.4, habilitado ARC

Hay muchas publicaciones sobre este tema. Ninguno parecía ser una solución clara para mí, así que pasé algunas horas probando y finalmente armé una solución que funciona para resolver el problema del OP:

"Estoy intentando descartar un UIAlertView antes de mostrar otro …"

Como el OP declaró que el método ".windows" ya no funcionará. Hay algunas otras forms en las que he leído sobre la creación de una categoría para UIAlertView y otras que usan notifications; sin embargo, eran demasiado complejos para mí.

Esto es lo que hay que hacer…

1) Conforme su class a UIAlertViewDelegate.

En el file '"* .h" de su class …

 @interface YourViewController : UIViewController <UIAlertViewDelegate> 

Esto permitirá que el object UIAlertView de su class envíe posts al siguiente método:

 - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex 

y para que el object UIAlertView de su class reciba posts del siguiente método:

 - (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated: 

Una palabra para el sabio, no tienes que hacerlo, en algunas situaciones conforma tu class a UIAlertViewDelegate, pero es la opción más segura. Todo depende de cómo usará su object en su class.

2) Declare el object UIAlertView como una variable de class o como una propiedad.

Algunas de las ventajas de crear una propiedad es que puede tener acceso a algunos captadores y creadores del object.

Como variable de instancia, en su class '"* .h" file …

 @interface YourViewController : UIViewController <UIAlertViewDelegate> { UIAlertView *yourAlertView; { //other properties @end 

Como propiedad (recomendado) en el file "* .h" de su class …

 @interface YourViewController : UIViewController <UIAlertViewDelegate> { //other instance variables { @property (strong, nonatomic) UIAlertView *yourAlertView; @end 

3) Evite generar references múltiples a su object UIAlertView.

Por ejemplo, si tiene un método que supervisa una condición determinada y muestra la alerta, no instale el object UIAlertView cada vez. En su lugar, instálelo una vez en -(void)viewDidLoad y -(void)viewDidLoad donde lo necesite. De lo contrario, esto evitará que

 - (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated: 

método de enviar el post deseado al object UIAlertView correcto.

4) Asigne la label al object UIAlertView y manipule las properties para cambiar el título, el post, etc.

 self.yourAlertView.title = @"some title string"; self.yourAlertView.message = @"some message string"; 

5) Muestra el object UIAlertView.

 [self.yourAlertView show]; 

6) Descartar antes de mostrar el object UIAlertView modificado.

 self.yourAlertView.title = @"some other title string"; self.yourAlertView.message = @"some other message string"; [self.yourAlertView show]; 

7) UIAlertView se deprecia en iOS8.

https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIAlertView_Class/index.html#//apple_ref/doc/uid/TP40006802-CH3-SW8

Importante: UIAlertView está en desuso en iOS 8. (Tenga en count que UIAlertViewDelegate también está en desuso.) Para crear y administrar alertas en iOS 8 y posteriores, use UIAlertController con un estilo preferido de UIAlertControllerStyleAlert.

En aplicaciones que se ejecutan en versiones de iOS anteriores a iOS 8, utilice la class UIAlertView para mostrar un post de alerta al usuario. Una vista de alerta funciona de manera similar pero difiere en apariencia de una hoja de acción (una instancia de UIActionSheet).

Utilice las properties y los methods definidos en esta class para configurar el título, el post y el delegado de una vista de alerta y configurar los botones. Debe configurar un delegado si agrega botones personalizados. El delegado debe cumplir con el protocolo UIAlertViewDelegate. Use el método show para mostrar una vista de alerta una vez que esté configurada.

Tuve el mismo problema y no quería save todas las posibles vistas de alerta como properties. Encontré una gran alternativa aquí :

 UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"Alert!" message:@"This alert will dismiss when application resigns active!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification* notification){ [alert dismissWithClickedButtonIndex:0 animated:NO]; }]; 

En el mismo hilo, como comentario a una de las publicaciones, hay una explicación de por qué el enfoque anterior no funciona en iOS 7:

En iOS7, Windows no contiene windows de vista de alerta. Están gestionados por otra stack de windows que no están expuestas.

Espero que ayude a alguien más. 🙂

Código para descartar toda la vista de alerta. Esta es la API PRIVADA, por lo que su aplicación puede ser rechazada por Apple cuando se sube a Appstore:

 Class UIAlertManager = objc_getClass("_UIAlertManager"); UIAlertView *topMostAlert = [UIAlertManager performSelector:@selector(topMostAlert)]; while (topMostAlert) { [topMostAlert dismissWithClickedButtonIndex:0 animated:NO]; topMostAlert = [UIAlertManager performSelector:@selector(topMostAlert)]; }