Динамические элементы UIMenuItem с @selector и динамическими методами

Я пытаюсь использовать UIMenuController для динамического меню (заголовки и действия поступают с сервера). Проблема в том, что я должен использовать UIMenuItems initWithTitle: action: где действие - это @selector.

Я могу использовать @selector (dispatch :), но тогда я не могу различить, какой из элементов нажал пользователь. - (void) отправка: (id) отправитель {NSLog (@ "% @", отправитель); } говорит, что это UIMenuController и у него нет метода, который бы сообщил, какой пункт меню был нажат.

Я не могу просто написать 100 методов для отправки всех возможных селекторов, хорошо, их не будет больше 10, но все же это кажется не очень хорошей идеей.

Нужно ли мне создавать динамические методы для каждого такого селектора? http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html? Это тоже кажется странным.

Есть ли лучшие предложения, чем эти два?

// Этот подход не работает.

- (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
}

// Такой подход действительно уродлив.

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

person Jeena    schedule 14.07.2010    source источник


Ответы (2)


Этот подход будет работать, хотя вам нужно уникальное имя селектора для каждой кнопки и сопоставление этого имени с тем, что вы хотите настроить.
Для имени селектора должна быть выбрана уникальная строка (UUID или, возможно, дезинфицированная и префиксная версия заголовка будет работать). Затем вам понадобится один метод, разрешающий вызов, и "псевдоним" с разными именами селекторов:

- (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], "v@:@");
        // now add menu item with sel as the action
    }
}

Теперь сервер пересылки может узнать, какой URL-адрес связан с пунктом меню:

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

Чтобы сгенерировать селекторы, вы можете использовать что-то вроде:

- (SEL)uniqueActionSelector {
    NSString *unique = ...; // the unique part
    NSString *selString = [NSString stringWithFormat:@"menu_%@:", unique];
    SEL sel = sel_registerName([selString UTF8String]);
    return sel;
}
person Georg Fritzsche    schedule 14.07.2010
comment
Откуда берется _cmd и что это такое? - person Jeena; 15.07.2010
comment
@jen: ключевое слово для селектора, с помощью которого вызывается функция реализации. Попробуйте войти NSStringFromSelector(_cmd). - person Georg Fritzsche; 15.07.2010

Если пункты меню не делают то же самое, почему должны совместно использовать действие? Я бы пошел дальше и написал действия, которые определяют желаемое поведение, и связал бы пункты меню с ними.

person Chuck    schedule 14.07.2010
comment
Проблема в том, что я не могу знать, какие действия будут выполняться во время компиляции, потому что действия будут исходить с сервера, это всегда заголовок меню и http-url, который должен вызываться, когда пользователь нажимает на него. Я дополню вопрос каким-нибудь кодом. - person Jeena; 15.07.2010