IOS UIMenuController UIMenuItem, how to determine

2020-02-05 09:26发布

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.

4条回答
女痞
2楼-- · 2020-02-05 09:46

Turns out it's possible to obtain the UIButton object (which is actually UICalloutBarButton) that represents UIMenuItem if you subclass UIApplication and reimplement -sendAction:to:from:forEvent:. Although only -flash selector goes through UIApplication, it's enough.

@interface MyApplication : UIApplication
@end

@implementation MyApplication
- (BOOL)sendAction:(SEL)action to:(id)target from:(id)sender forEvent:(UIEvent *)event
{
    // target == sender condition is just an additional one
    if (action == @selector(flash) && target == sender && [target isKindOfClass:NSClassFromString(@"UICalloutBarButton")]) {
        NSLog(@"pressed menu item title: %@", [(UIButton *)target titleLabel].text);
    }
    return [super sendAction:action to:target from:sender forEvent:event];
}
@end

You can save target (or any data you need from it) in e.g. property and access it later from your UIMenuItem's action.

And to make your UIApplication subclass work, you must pass its name as a third parameter to UIApplicationMain():

int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, NSStringFromClass([MyApplication class]), NSStringFromClass([YOUR_APP_DELEGATE class]));
    }
}

This solution works on iOS 5.x-7.0 as of post date (didn't test on older versions).

查看更多
够拽才男人
3楼-- · 2020-02-05 09:53

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];
    }
}
查看更多
手持菜刀,她持情操
4楼-- · 2020-02-05 09:56

ort11, you might want to create a property of myuimenuitem and set some sort of Tag. Thay way the object of sender could be recognized by its tag it. In Ibaction then you can set a switch statement that can correspond to each sender.tag and work throught that logic. I guess thats the simplest way to go.

查看更多
The star\"
5楼-- · 2020-02-05 10:12

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.

查看更多
登录 后发表回答