Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IOS UIMenuController UIMenuItem, how to determine item selected with generic selector method

With the following setup

....
MyUIMenuItem *someAction  = [[MyUIMenuItem alloc]initWithTitle : @"Something"  action : @selector(menuItemSelected:)];
MyUIMenuItem *someAction2 = [[MyUIMenuItem alloc]initWithTitle : @"Something2" action : @selector(menuItemSelected:)];
....

- (IBAction) menuItemSelected : (id) sender
{
    UIMenuController *mmi = (UIMenuController*) sender;
}

How to figure out which menu item was selected.

And don't say that you need to have two methods... Thanks in advance.

like image 744
ort11 Avatar asked Feb 05 '12 03:02

ort11


2 Answers

Okay, I've solved this one. The solution isn't pretty, and the better option is "Apple fixes the problem", but this at least works.

First of all, prefix your UIMenuItem action selectors with "magic_". And don't make corresponding methods. (If you can do that, then you don't need this solution anyway).

I'm building my UIMenuItems thus:

NSArray *buttons = [NSArray arrayWithObjects:@"some", @"random", @"stuff", nil];
NSMutableArray *menuItems = [NSMutableArray array];
for (NSString *buttonText in buttons) {
    NSString *sel = [NSString stringWithFormat:@"magic_%@", buttonText];
    [menuItems addObject:[[UIMenuItem alloc] 
                         initWithTitle:buttonText
                         action:NSSelectorFromString(sel)]];
}
[UIMenuController sharedMenuController].menuItems = menuItems;

Now your class that catches the button tap messages needs a few additions. (In my case the class is a subclass of UITextField. Yours might be something else.)

First up, the method that we've all been wanting to have but that didn't exist:

- (void)tappedMenuItem:(NSString *)buttonText {
    NSLog(@"They tapped '%@'", buttonText);
}

Then the methods that make it possible:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    NSString *sel = NSStringFromSelector(action);
    NSRange match = [sel rangeOfString:@"magic_"];
    if (match.location == 0) {
        return YES;
    }
    return NO;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    if ([super methodSignatureForSelector:sel]) {
        return [super methodSignatureForSelector:sel];
    }
    return [super methodSignatureForSelector:@selector(tappedMenuItem:)];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    NSString *sel = NSStringFromSelector([invocation selector]);
    NSRange match = [sel rangeOfString:@"magic_"];
    if (match.location == 0) {
        [self tappedMenuItem:[sel substringFromIndex:6]];
    } else {
        [super forwardInvocation:invocation];
    }
}
like image 88
sobri Avatar answered Nov 05 '22 08:11

sobri


One would expect that the action associated with a given menu item would include a sender parameter that should point to the chosen menu item. Then you could simply examine the title of the item, or do as kforkarim suggests and subclass UIMenuItem to include a proeprty that you can use to identify the item. Unfortunately, according to this SO question, the sender parameter is always nil. That question is over a year old, so things may have changed -- take a look at what you get in that parameter.

Alternately, it looks like you'll need to a different action for each menu item. Of course, you could set it up so that all your actions call a common method, and if they all do something very similar that might make sense.

like image 43
Caleb Avatar answered Nov 05 '22 07:11

Caleb