2010-07-14 16 views
8

Estoy tratando de usar UIMenuController para un menú dinámico (los títulos y las acciones provienen de un servidor). El problema es que tengo que usar UIMenuItems initWithTitle: action: donde action es un @selector.UIMenuItems dinámicos con @selector y métodos dinámicos

Puedo usar @selector (dispatch :) pero no puedo distinguir cuáles de los elementos presionó el usuario. - (void) dispatch: (id) remitente {NSLog (@ "% @", remitente); } dice que es un UIMenuController y no tiene un método que indique qué elemento del menú se presionó.

No puedo escribir 100 métodos para enviar todos los posibles selectores, vale, no habrá más de 10, pero aún así, esto no parece una buena idea.

¿Tengo que crear métodos dinámicos para cada uno de esos selectores? http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html? Esto parece extraño también.

¿Alguna mejor propuesta que estas dos?

// Este enfoque no funciona.

- (void)showMenu { 

    [self becomeFirstResponder]; 

    NSMutableArray *menuItems = [[NSMutableArray alloc] init]; 

    UIMenuItem *item; 
    for (MLAction *action in self.dataSource.actions) { 
     item = [[UIMenuItem alloc] initWithTitle:action.title action:@selector(action:)]; 
     [menuItems addObject:item]; 
     [item release]; 
    } 

    UIMenuController *menuController = [UIMenuController sharedMenuController]; 
    menuController.menuItems = menuItems; 
    [menuItems release]; 
    [menuController update]; 
    [menuController setMenuVisible:YES animated:YES]; 

} 

- (void)action:(id)sender { 
    NSLog(@"%@", sender); // gives UIMenuController instead of UIMenuItem 
    // I can not know which menu item was pressed 
} 

// Este enfoque es realmente feo.

- (void)showMenu { 

    [self becomeFirstResponder]; 

    NSMutableArray *menuItems = [[NSMutableArray alloc] initWithCapacity:5]; 

    UIMenuItem *item; 
    NSInteger i = 0; 
    for (MLAction *action in self.dataSource.actions) { 
     item = [[UIMenuItem alloc] initWithTitle:action.text 
                      action:NSSelectorFromString([NSString stringWithFormat:@"action%i:", i++])]; 
     [menuItems addObject:item]; 
     [item release]; 
    } 

    UIMenuController *menuController = [UIMenuController sharedMenuController]; 
    menuController.menuItems = menuItems; 
    [menuItems release]; 
    [menuController update]; 
    [menuController setMenuVisible:YES animated:YES]; 

} 

- (void)action:(NSInteger)number { 
    NSLog(@"%i", number); // gives the index of the action in the menu. 
} 

// This is a hack, I have to assume that there will never be more then 15 actions 
- (void)action0:(id)sender { [self action:0]; } 
- (void)action1:(id)sender { [self action:1]; } 
- (void)action2:(id)sender { [self action:2]; } 
- (void)action3:(id)sender { [self action:3]; } 
- (void)action4:(id)sender { [self action:4]; } 
- (void)action5:(id)sender { [self action:5]; } 
- (void)action6:(id)sender { [self action:6]; } 
- (void)action7:(id)sender { [self action:7]; } 
- (void)action8:(id)sender { [self action:8]; } 
- (void)action9:(id)sender { [self action:8]; } 
- (void)action10:(id)sender { [self action:10]; } 
- (void)action11:(id)sender { [self action:11]; } 
- (void)action12:(id)sender { [self action:12]; } 
- (void)action13:(id)sender { [self action:13]; } 
- (void)action14:(id)sender { [self action:14]; } 

Respuesta

10

Ese enfoque funcionaría, aunque necesita un nombre de selector exclusivo para cada botón y un mapeo desde ese nombre al objeto que desee.
Para el nombre del selector, se debe elegir una cadena única (los UUID o quizás una versión del título con sanitización & funcionaría). A continuación, necesita un método que resuelve la llamada y "alias" con los diferentes nombres de selector:

- (void)updateMenu:(NSArray *)menuEntries { 
    Class cls = [self class]; 
    SEL fwd = @selector(forwarder:); 
    for (MenuEntry *entry in menuEntries) { 
     SEL sel = [self uniqueActionSelector]; 
     // assuming keys not being retained, otherwise use NSValue: 
     [self.actionDict addObject:entry.url forKey:sel]; 
     class_addMethod(cls, sel, [cls instanceMethodForSelector:fwd], "[email protected]:@"); 
     // now add menu item with sel as the action 
    } 
} 

Ahora el transportista puede buscar lo que la URL está asociado con el elemento de menú:

- (void)forwarder:(UIMenuController *)mc { 
    NSLog(@"URL for item is: %@", [actionDict objectForKey:_cmd]); 
} 

Para generar los selectores que podría utilizar algo como:

- (SEL)uniqueActionSelector { 
    NSString *unique = ...; // the unique part 
    NSString *selString = [NSString stringWithFormat:@"menu_%@:", unique]; 
    SEL sel = sel_registerName([selString UTF8String]); 
    return sel; 
} 
+0

¿De dónde viene el _cmd y qué es? – Jeena

+2

@jen: palabra clave para el selector con el que se invoca la función de implementación. Intente registrar 'NSStringFromSelector (_cmd)'. –

+1

Awesome work Chicos, muchas gracias. – sachin

0

A menos que los elementos del menú hacen lo mismo, ¿por qué deberían comparten una acción? Continuaría escribiendo acciones que especifiquen el comportamiento que desea y vincule los elementos del menú a esos.

+1

El problema es que no puedo saber qué acciones habrá en tiempo de compilación porque las acciones se c Desde un servidor, siempre es un título para el menú y una URL http a la que se debe llamar cuando el usuario haga clic en ella. Actualizaré la pregunta con un código. – Jeena