21

I want to show 2 options like "hi" & "bye" when user completes selection on UIWebView.

I have added observer to my view controller as follows. But I don't know further implementation.

[[UIMenuController sharedMenuController] addObserver:self 
                                          forKeyPath:UIMenuControllerWillShowMenuNotification
                                             options:nil
                                             context:nil
 ];
halfer
  • 19,824
  • 17
  • 99
  • 186
sagarkothari
  • 24,520
  • 50
  • 165
  • 235

2 Answers2

44

Sagar,

Your question is a couple of months old, but I finally figured this one out, so I figured I'd answer it in case it helps out someone else.

I added the following code to the viewDidAppear: method of the view controller that contains the webview.

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    UIMenuItem *customMenuItem1 = [[[UIMenuItem alloc] initWithTitle:@"Custom 1" action:@selector(customAction1:)] autorelease];
    UIMenuItem *customMenuItem2 = [[[UIMenuItem alloc] initWithTitle:@"Custom 2" action:@selector(customAction2:)] autorelease];
    [[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObjects:customMenuItem1, customMenuItem2, nil]];
}

In my viewDidDisappear:, I go ahead and remove those items:

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    [[UIMenuController sharedMenuController] setMenuItems:nil];
}

Then, I implemented the canPerformAction:withSender: method in the view controller. It helps to understand the concept of responders and responder chains to understand what is going on here. Basically, your uiviewcontroller is part of the responder chain, so it gets asked if it can handle any actions (like your custom actions you added above) that objects higher up the responder chain (like the UIWebView) don't know how to handle (see the UIResponder documentation and the Event Handling Guide for iOS for the gory details).

Now, when canPerformAction:withSender: is called for the webview, the sender parameter is set to nil. So, I try to be a bit clever about how I write this function. Basically, I make sure that the sender is nil, I'm showing the webview to the user, and any other controls on the page aren't the first responder. If that's the case, then I check to see if this is one of the actions I defined above and retur YES if it is. In all other cases I return the default value from UIViewController by calling the same method on super.

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    if (webView.superview != nil && ![urlTextField isFirstResponder]) {
        if (action == @selector(customAction1:) || action == @selector(customAction2:)) {
            return YES;
        }
    }

    return [super canPerformAction:action withSender:sender];
}

Of course, now the next step is figuring out how to actually do something with the selection (probably by running some JavaScript in the webview).

Sophie Alpert
  • 139,698
  • 36
  • 220
  • 238
Jacques
  • 6,050
  • 1
  • 31
  • 24
  • You can still award me a bounty if you want :-) – Jacques Sep 18 '10 at 22:00
  • 1
    I removed the check for sender == nil because future versions of iOS might set sender to something other than nil – Jacques Sep 24 '10 at 22:50
  • I would like to invite you to answer this question http://stackoverflow.com/questions/31183894/uimenucontroller-method-settargetrectinview-not-working-in-uitableview – S.J Jul 06 '15 at 05:33
  • 1
    @benalpert I would like to invite you to answer this question http://stackoverflow.com/questions/31183894/uimenucontroller-method-settargetrectinview-not-working-in-uitableview – S.J Jul 06 '15 at 05:34
  • what is urlTextField here? – Rajneesh071 Sep 16 '15 at 06:17
5

In swift:

class ViewController: UIViewController {
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        // add two custom menu items to the context menu of UIWebView (assuming in contenteditable mode)
        let menuItem1 = UIMenuItem(title: "Foo", action: #selector(ViewController.foo))
        let menuItem2 = UIMenuItem(title: "Bar", action: #selector(ViewController.bar))
        UIMenuController.sharedMenuController().menuItems = [menuItem1, menuItem2]
    }

    override func viewDidDisappear(animated: Bool) {
        super.viewDidAppear(animated)
        UIMenuController.sharedMenuController().menuItems = nil
    }

    override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
        if webView?.superview != nil {
            if action == #selector(ViewController.foo) || action == #selector(ViewController.bar) {
                return true
            }
        }

        return super.canPerformAction(action, withSender: sender)
    }

    func foo() {
        print("foo")
    }

    func bar() {
        print("bar")
    }
}

Note: #selector is available in Swift 2.2.

screenshot

Community
  • 1
  • 1
parag
  • 937
  • 13
  • 11