Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Speech to custom UIMenuController

Tags:

ios

iphone

I created a custom UIMenuController in a UIWebView but it seems to get rid of the "Speak Selection" option in the UIMenuController after that. The speak selection option is turned on in Preferences on all test devices and it appears in other apps, including non-Apple apps. Is there an accessibility service or part of the sharedMenuController that I can call to get this item?

UIMenuItem *copyMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Copy", @"Copy menu item") action:@selector(myappCopy:)];

UIMenuItem *highlightMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Highlight", @"Highlight menu option") action:@selector(myappHighlight:)];

UIMenuItem *unhighlightMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Remove Highlight", @"Remove Highlight menu option")
                                                           action:@selector(myappRemoveHighlight:)];

UIMenuItem *noteMenuItem = [[UIMenuItem alloc] initWithTitle:NSLocalizedString(@"Note", @"Note menu options") action:@selector(myappNote:)];

[UIMenuController sharedMenuController].menuItems = [NSArray arrayWithObjects:copyMenuItem, highlightMenuItem, unhighlightMenuItem, noteMenuItem, nil];

[copyMenuItem release];
[highlightMenuItem release];
[unhighlightMenuItem release];
[noteMenuItem release];

I even tried to parse the existing shared menu items at the start, but I don't see anything dumped in the log. The method is getting called on app launch.

Tried this at top of method:

    for (UIMenuItem *menuItem in [UIMenuController sharedMenuController].menuItems) {
        NSLog(@"title: %@", menuItem.title);
        NSLog(@"action: %@", menuItem.action);
    }

Any help is much appreciated! Thanks - Eric

like image 899
Eric Welander Avatar asked Mar 26 '12 21:03

Eric Welander


2 Answers

I've found some interesting things with this bug. Basically, when speak selection is enabled, after you make the first selection the UIMenuController is emptied of menuItems. The solution, though hacky, is simple.

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
NSString *selectorString = NSStringFromSelector(action);
BOOL isAccessibilitySelector = [selectorString isEqualToString:@"_accessibilitySpeak:"] || [selectorString isEqualToString:@"_accessibilityPauseSpeaking:"];
if (isAccessibilitySelector && [super canPerformAction:action withSender:sender]) {
    //(re)add menuItems to UIMenuController
    return YES;
}
return NO;

}

I should note that you must re-add the menuItems after their canPerformAction...() has been called.

I have submitted this as radar:12931434. Update: DUP'ed to 13060693.

like image 100
Matthew Leffler Avatar answered Nov 01 '22 18:11

Matthew Leffler


Some of the UIMenuController items can be found in UIResponder.h in UIKit framework.

@interface NSObject(UIResponderStandardEditActions)   // these methods are not implemented     in NSObject

 - (void)cut:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
 - (void)copy:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
 - (void)paste:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
 - (void)select:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
 - (void)selectAll:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
 - (void)delete:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);
 - (void)makeTextWritingDirectionLeftToRight:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);
 - (void)makeTextWritingDirectionRightToLeft:(id)sender __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0);

 @end

But there is no speak text option there. It turns out if you override "canPerformAction: WithSelector:", within a subclass of your UIWebView or UITextField as listed below, you will also get a listing of all of the actions sent to self including the UIMenuController options.

// Override
- (BOOL) canPerformAction:(SEL)action withSender:(id)sender
{
    NSLog(@"%@",NSStringFromSelector(action));

    //if you are customizing your menu, return NO except for your specific selectors
    return YES;
}

You'll find several methods that may interest you, including _accessibilitySpeak: and _accessibilityPauseSpeaking: and _define: (please note these three selectors are iOS 5 only). The underscore means that they are private, so also keep in mind that you can't directly call them with the classic [class selector] syntax.

Remember, these are system menuItems, which means Apple will stick them in front of any menu items you add, often leaving your menu items in a second layer accessed by tapping the > arrow. If you want to control the order in which the items are display, and/or mix Apple's system items with your items, you will need to create custom menu items for these actions that call a method in your class like this:

- (void) myAppSpeak: (UIMenuController*) sender
{
    [super performSelector:@selector(_accessibilitySpeak:)];
}

Keep in mind that these methods need to be implemented in a subclass of a class that implements these already, such as a sub class of UIWebView....not a subclass of UIWebViewController.

Then inside the controller, or wherever you build your UIMenuController, create the custom button that calls this method. Be sure if you are in a web view, you are referencing an object of type of your subclass, and not the generic webview. Otherwise, it won't work.

    UIMenuItem *speakMenuItem = [[UIMenuItem alloc] initWithTitle:@"Speak" action:@selector(myAppSpeak:)];

    [UIMenuController sharedMenuController].menuItems = [NSArray arrayWithObjects:speakMenuItem, etc. etc., nil];

Even though you are adding it to the your menu items, it will not appear unless you return YES for the selector in your canPerformAction: WithSelector: in your subclass of your web view or text field. So feel free to add items here that may be circumstantial otherwise. You can use logic in your subclassed view to sort that out.

like image 42
Eric Welander Avatar answered Nov 01 '22 18:11

Eric Welander