2009-06-30 8 views
15

En mi aplicación utilizo UIAlertView para mostrar al usuario un mensaje y algunas opciones. Dependiendo del botón presionado, quiero que la aplicación realice algo en un objeto. El código de ejemplo que utilizo es ...¿Cómo pasar con seguridad un objeto de contexto en un delegado de UIAlertView?

-(void) showAlert: (id) ctx { 
    UIAlertView *baseAlert = [[UIAlertView alloc] 
          initWithTitle: title 
          message: msg 
          delegate:self 
          cancelButtonTitle: cancelButtonTitle 
          otherButtonTitles: buttonTitle1, buttonTitle2, nil]; 
    //baseAlert.context = ctx; 
    [baseAlert show]; 
    [baseAlert release]; 
} 


- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { 
    if (buttonIndex == 1) { 
     id context = ...;//alertView.context; 
     [self performSelectorOnMainThread:@selector(xxx:) withObject: context waitUntilDone: NO]; 
    } 
} 

¿Hay alguna manera de pasar un objeto en el delegado como un objeto de contexto? o tal vez de alguna otra manera?

Podría agregar la propiedad en el delegado pero el mismo objeto delegado está siendo utilizado por muchas vistas de alerta diferentes. Por este motivo, preferiría una solución en la que el objeto de contexto se adjunte a la instancia de UIAlertView y se transmita al delegado como parte del objeto UIAlertView.

Respuesta

12

Sigo pensando que almacenarla localmente es la mejor solución. Cree una variable NSMutableDictionary local de clase para contener un mapa de objetos de contexto, almacene el contexto con UIAlertView como la clave y el contexto como el valor.

Luego, cuando se llama al método de alerta, simplemente mire en el diccionario para ver qué objeto de contexto está relacionado. Si no desea utilizar la totalidad del objeto de alerta como una clave, puede utilizar sólo la dirección del objeto UIAlertView:

NSString *alertKey = [NSString stringWithFormat:@"%x", baseAlert]; 

La dirección debe ser constante en el teléfono. O puede etiquetar cada alerta como el otro cartel sugirió y usar la etiqueta para buscar un contexto en el mapa.

¡No olvide borrar el objeto de contexto cuando haya terminado!

+0

"La dirección debe ser constante en el teléfono" <- dicha dirección es un puntero a un objeto, y como tal no se puede confiar en que se mantenga constante. El resto de su respuesta es sensata, solo quería señalar esto para referencia futura, en caso de que alguien más aterrizara en esta página. – Morpheu5

0

Puede subclase UIAlertView y añada la propiedad allí.

7

también puede usar la propiedad de etiqueta (ya que es una subclase de UIView). Esto es solo una int, pero puede ser suficiente para ti.

10

Una aplicación completa que le permite pasar contexto:

@interface TDAlertView : UIAlertView 
@property (nonatomic, strong) id context; 
@end 

@implementation TDAlertView 
@end 

Y un ejemplo de uso, tenga en cuenta la forma en que premoldeado el puntero:

@implementation SomeAlertViewDelegate 
- (void)alertView:(TDAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { 
    NSLog(@"%@", [alertView context]); 
} 
@end 
+4

Me pregunto por qué no tienen algo como esto integrado. Parece que pasarle contexto a un delegado sería un problema bastante común – pepsi

+0

Como mencionó @Jeffery Thomas, la subclase de UIAlertView no es una buena idea. –

+1

@JoshBrown mi subclase está absolutamente bien, no modifica nada sobre la clase, que es lo que la documentación está advirtiendo. Efectivamente, la subclase es solo azúcar sintáctica. – mxcl

3

subclases UIAlertView no es una buena idea.

http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIAlertView_Class/UIAlertView/UIAlertView.html

subclassing Notas

La clase UIAlertView está destinado a ser utilizado tal cual y no es compatible con la subclasificación. La jerarquía de vista para esta clase es privada y no debe modificarse.

UIAlertView with a user supplied context and [self autorelease] responde a esta pregunta de otra manera.

+0

Precisamente como dice la documentación: está bien hacer una subclase siempre que no anule nada. – mxcl

+0

@MaxHowell La línea en la documentación es "no admite subclases". No puede permitir implícitamente algo que explícitamente prohíbe. La prohibición de modificar la jerarquía de vistas se suma a la prohibición de crear subclases. –

+0

Bueno, supongo, la interpretación es la interpretación. De acuerdo con mi experiencia y conocimiento sobre cómo objc funciona bajo el capó, y cómo Apple documenta sus clases, estoy 100% seguro de que puede subclase UIAlertView de la manera en que lo hago en mi respuesta. – mxcl

1

Hice una mezcla entre la respuesta de Kendall y los usos de los bloques en una de mis clases de controlador de vista base. Ahora puedo usar AlertView y ActionSheets con bloques que mejoran enormemente la legibilidad. Así es como lo hice:

En la.h de mi ViewController que declarar un tipo de bloque (opcional pero recommanded)

typedef void (^AlertViewBlock)(UIAlertView*,NSInteger); 

También declaro un dictionnary mutable que almacenará los bloques para cada alertview:

NSMutableDictionary* m_AlertViewContext; 

En el archivo de implementación agrego una método para crear la AlertView y guardar el bloque:

-(void)displayAlertViewWithTitle:(NSString *)title message:(NSString *)message cancelButtonTitle:(NSString *)cancelButtonTitle withBlock:(AlertViewBlock)execBlock otherButtonTitles:(NSArray *)otherButtonTitles 
    { 
     UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title 
                 message:message 
                 delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles: nil]; 
     for (NSString* otherButtonTitle in otherButtonTitles) { 
      [alert addButtonWithTitle:otherButtonTitle]; 
     } 
     AlertViewBlock blockCopy = Block_copy(execBlock); 
     [m_AlertViewContext setObject:blockCopy forKey:[NSString stringWithFormat:@"%p",alert]]; 
     Block_release(blockCopy); 
     [alert show]; 
     [alert release]; 
    } 

Nota que recibo los mismos atributos como el constructor de la UIAlertView pero el delegado (que será s duende). También recibo un objeto AlertViewBlock que guardo en el dictionnario mutable m_AlertViewContext. Luego muestro la alerta como solía hacer.

En las devoluciones de llamada de delegados, que llamo el bloque y proporcionándole los parámetros:

#pragma mark - 
#pragma mark UIAlertViewDelegate 

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 
{ 
    NSString* blockKey = [NSString stringWithFormat:@"%p",alertView]; 
    AlertViewBlock block = [m_AlertViewContext objectForKey:blockKey]; 
    block(alertView,buttonIndex); 
    [m_AlertViewContext removeObjectForKey:blockKey]; 
} 

- (void)alertViewCancel:(UIAlertView *)alertView { 
    NSString* blockKey = [NSString stringWithFormat:@"%p",alertView]; 
    [m_AlertViewContext removeObjectForKey:blockKey]; 
} 

Ahora, cada vez que necesito utilizar un AlertView puedo llamarlo así:

[self displayAlertViewWithTitle:@"Title" 
           message:@"msg" 
         cancelButtonTitle:@"Cancel" 
     withBlock:^(UIAlertView *alertView, NSInteger buttonIndex) { 
      if ([[alertView buttonTitleAtIndex:buttonIndex] isEqualToString:@"DO ACTION"]){ 
       [self doWhatYouHaveToDo]; 
      } 
     } otherButtonTitles:[NSArray arrayWithObject:@"DO ACTION"]]; 

I hizo lo mismo con la hoja de acción y ahora es realmente fácil de usar. Espero que ayude.

3

En lugar de debatir el significado de "no admite subclases", proporcionaré una mejor respuesta. Creé una categoría genérica de contextInfo para mi trabajo hace un par de meses. Acabo de ponerlo en github: JLTContextInfo.

#import "objc/runtime.h" 

@interface NSObject (JLTContextInfo) 
- (NSMutableDictionary *)JLT_contextInfo; 
@end 
@implementation NSObject (JLTContextInfo) 
- (NSMutableDictionary *)JLT_contextInfo 
{ 
    static char key; 
    NSMutableDictionary *contextInfo = objc_getAssociatedObject(self, &key); 
    if (!contextInfo) { 
     contextInfo = [NSMutableDictionary dictionary]; 
     objc_setAssociatedObject(self, &key, contextInfo, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
    } 
    return contextInfo; 
} 
@end 

Esto crea un lugar para almacenar fácilmente datos adicionales para cualquier objeto derivado de NSObject. Ahora la respuesta es casi idéntica a la pregunta original.

-(void) showAlert: (id) ctx { 
    UIAlertView *baseAlert = [[UIAlertView alloc] 
          initWithTitle: title 
          message: msg 
          delegate:self 
          cancelButtonTitle: cancelButtonTitle 
          otherButtonTitles: buttonTitle1, buttonTitle2, nil]; 
    [[baseAlert JLT_contextInfo] setObject:ctx forKey:@"ctx"]; 
    [baseAlert show]; 
    [baseAlert release]; 
} 

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { 
    if (buttonIndex == 1) { 
     id context = [[alertView JLT_contextInfo] objectForKey:@"ctx"]; 
     [self performSelectorOnMainThread:@selector(xxx:) withObject: context waitUntilDone: NO]; 
    } 
} 
1

From my other answer, aquí es una solución rápida y limpia que se aprovecha de objetos asociados. Menciono en mi otra respuesta que incluso se podría reemplazar con UIAlertViewNSObject y eficaz añadir una propiedad context a cualquier objeto:

#import <objc/runtime.h> 

@interface UIAlertView (Private) 
@property (nonatomic, strong) id context; 
@end 

@implementation UIAlertView (Private) 
@dynamic context; 
-(void)setContext:(id)context { 
    objc_setAssociatedObject(self, @selector(context), context, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 
-(id)context { 
    return objc_getAssociatedObject(self, @selector(context)); 
} 
@end 

Y entonces usted será capaz de hacer algo como:

NSObject *myObject = [NSObject new]; 

UIAlertView *alertView = ... 
alertView.context = myObject; 

IMPORTANTE: Y no se olvide de borrar el contexto en dealloc !!

Cuestiones relacionadas