Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing Copy, Look Up, and Share from UIMenuController

I am trying to override the default UIMenuController so that only my custom item "Define..." appears when the user selects text in its text view. I haven't had much luck with the approaches I've found online thus far.

To be more specific, I have subclassed a UIViewController and used canPerformAction() to exclude all actions except my define method.

override func becomeFirstResponder() -> Bool {
    return true
}

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    let canPerform: Bool
    if action == #selector(defineWord){
        canPerform = true
    }
    else {
        canPerform = false
    }

    print("action = \(action), canPerform = \(canPerform)")
    return canPerform
}

In the view controller's viewDidLoad(), I've included the following:

let shared = UIMenuController.shared
let menuItemDefine = UIMenuItem(title: "Define...", action: #selector(self.defineWord))
shared.menuItems = [menuItemDefine]

Whenever I select text in the view, the console goes through each possible action that might appear in the UIMenuController and says they can't be performed, with the exception of my custom action:

action = cut:, canPerform = false
action = select:, canPerform = false
(and so on, until...)
action = defineWord, canPerform = true

But the resulting edit menu contains "Copy", "Look Up", "Share", and "Define...". These don't appear in the console, which makes me think that a different approach is called for.

Note that I've also tried subclassing UITextView and using the above code as appropriate, but the result is the same.

Any ideas where I'm going wrong?

like image 822
Gaius Davidius Avatar asked Jan 05 '23 06:01

Gaius Davidius


2 Answers

This might help everyone who is asking this question that how to remove "Copy", "Select All" etc.. standard menu items or UIResponderStandardEditActions that are still visible when you have already returned false in canPerformAction:.

It is related to responder chain. As canPerformAction: is called for every responder, for some of those it may be returning true in canPerformAction: as a default value.

Thus to check where it is failing I found it by overriding this canPerformAction: for every element I used in my controller

For example in my view controller I had a webview and the mistake I was doing was that I was overriding the canPerformAction: in the delegate methods i.e I was doing something like below

extension viewcontroller: UIWebViewDelegate{
    open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return false
    }
}

But the point is that you have to do it for element and not as the delegate method.

extension UIView {

    func dropRoundCorners() {
        self.layer.cornerRadius = 10.0;
        self.clipsToBounds = true;
    }

    open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return false
    }
}

extension UIImageView{
    open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return false
    }
}

extension UIScrollView{
    open override func canPerformAction(_ action: Selector, withSender 
sender: Any?) -> Bool {
        return false
    }
}

extension UISlider{
    open override func canPerformAction(_ action: Selector, withSender 
sender: Any?) -> Bool {
        return false
    }
}

extension UIWebView{
   open override func canPerformAction(_ action: Selector, withSender 
sender: Any?) -> Bool {
        return false
    }
}

I hope this is useful to anyone whose is stuck with this issue.

Following are links that might help you with details:

UIResponder reference

very important read the discussion here regarding responder

some what related

like image 140
Gaurav Avatar answered Jan 13 '23 10:01

Gaurav


May be it is too late for the answer, but it can be helpful for other users. So, my solution is: I created custom UITextView and redefined the following methods:

- (BOOL)canBecomeFirstResponder {
    return YES;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    //Here you can check for all action what you need
    return (action == @selector(yourCustomAction)) ? YES : NO;
}
like image 21
Vlad Semenov Avatar answered Jan 13 '23 08:01

Vlad Semenov