UIMenuController do not hide system items - ios

I show a UIMenuController in a uiviewconroller in this way:
in my class:
override open func canBecomeFirstResponder() -> Bool {
return true
}
open override func canPerformAction(_ action: Selector, withSender sender: Any) -> Bool {
//here I check for my custom action, else return false
return false
}
then to show I use:
//Make this as first responder
self.becomeFirstResponder()
///Build menu
let menu = UIMenuController.shared
///Set item and anchor point, and showit
menu.menuItems = itemsToAdd
menu.setTargetRect(CGRect(x: 0, y: 5, width: bubbleNode.view.bounds.size.width, height: bubbleNode.view.bounds.size.height), in: bubbleImageNode.view)
menu.setMenuVisible(true, animated: true)
the problem is that in a device I show my custom items, but also: "Spell, speak, speck sentence, ecc..." how can I disable it?

override canPerformAction and handle it for each specific action. It working perfectly for me.
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
var canPerform = super.canPerformAction(action, withSender: sender)
if (action == "your action to restrict") {
canPerform = false
}
return canPerform
}

Ok, the problem is "Accessibility" option "speak selection" in my device, if I disabled it, I see only the custom items, but in other app i see only custom items with this option enabled!

class TextViewWithCopyAction: UITextView {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(copy(_:)){
return true
} else {
return false
}
}
}
to use it:
let textView: TextViewWithCopyAction = {
let textView = TextViewWithCopyAction()
textView.backgroundColor = .white
return textView
}()

If you see speak option in UIMenuController that is from device go to:
Setting->Accessibility->Speech->Speech
Select and disable it. Automatically Speak option will get removed from menu.

Related

Show the copy menu on a view

I am trying to show the ios copy button on a view and handle the click on a custom function. I tried to display the button with this code but nothing is appearing.
let menu = UIMenuController.shared
if !menu.isMenuVisible {
menu.setTargetRect(paragraphTableViewCell.bounds, in: paragraphTableViewCell)
menu.setMenuVisible(true, animated: true)
}
EDIT:
I got this errors
You need to call canBecomeFirstResponder In your class.
And override canPerformAction then add your appropriate option as UIMenuItem
func canBecomeFirstResponder() -> Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any) -> Bool {
if action == #selector(self.cut) {
return false
}
else if action == #selector(self.copy) {
return true
}
else if action == #selector(self.paste) {
return false
}
else if action == #selector(self.select) || action == #selector(self.selectAll) {
return true
}
else {
return super.canPerformAction(action, withSender: sender)
}
}
override func copy(_ sender: Any?) {
}
Finally you should pass UIView object
menu.setTargetRect(paragraphTableViewCell.bounds, in: paragraphTableViewCell.contentView)
Because its required
- (void)setTargetRect:(CGRect)targetRect inView:(UIView *)targetView;

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?
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
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;
}

How to use More menucontroller in View controller

Can Anyone help me, i'm having problem with UIMenucontroller.In here, i have to use two menucontroller in single viewcontroller.
For First menu only "paste",for other menu "copy","select","select all" When i'm using shared menucontroller it affects the other menu.
My code for first menu is as follows:
override func canBecomeFirstResponder() -> Bool
{
return true
}
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool
{
//actions
}
UIMenuController.sharedMenuController().menuItems = nil
let Select: UIMenuItem = UIMenuItem(title: "Select", action: Selector("Select"))
let SelectAll: UIMenuItem = UIMenuItem(title: "SelectAll", action: Selector("SelectAll"))
let Copy: UIMenuItem = UIMenuItem(title: "Copy", action: Selector("Copy"))
let menu: UIMenuController = UIMenuController.sharedMenuController()
menu.menuItems = [Select,SelectAll,Copy]
menu.setTargetRect(cell.frame, inView: cell.superview!)
menu.setMenuVisible(true, animated: true)
and my second menu is:
UIMenuController.sharedMenuController().menuVisible = false
let paste: UIMenuItem = UIMenuItem(title: "Paste", action: Selector("paste"))
let menu: UIMenuController = UIMenuController.sharedMenuController()
menu.menuItems = [paste]
menu.setTargetRect(message_Textfield.frame, inView: message_Textfield.superview!)
menu.setMenuVisible(true, animated: true)
Error:
In here,in second menu contains unwanted things as [Select,SelectAll,Copy] with [Paste].
How can i resolve this,thanks in advance
You should override canPerformAction in a UITextField subclass to disable the item you don't want, then assign each uitextfield you created to the subclass.
For example, disable paste menu item in uimenucontroller:
class CustomTextField: UITextField {
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == "paste:" {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
Usage:
let message_Textfield = CustomTextField()
Now paste menu item will be disabled for message_Textfield
Hi Thanks for your answer,But i found solution as follows:
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool
{
if(MenuBool == true){
if action == Selector("Copy") || action == Selector("star") || action == Selector("info") || action == Selector("forward") || action == Selector("Delete")
{
print("UIMenuController====>CellMenu")
UIMenuController.sharedMenuController().menuVisible = false
return true
}
print("UIMenuController====>Defaultmenu1")
return false
}else if MenuBool == false
{
print("UIMenuController====>Defaultmenu2")
return false
}else{
print("UIMenuController====>DefaultmenuElse")
return false
}
}
In,this manner working fine.
:):):)

How do I add a custom contextual menu item to UITextView using Swift?

I managed to subclass UITextView and disabled the "Define" contextual menu item.
class TextViewer: UITextView {
// Overide, disable the "Define" contextual menu item
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if action == "_define:" {
return false;
}
return super.canPerformAction(action, withSender: sender)
}
}
But now, how do I add a different/custom menu item to take its place? I was guessing it might be something like this, but it's not quite right.
var menuController = UIMenuController.sharedMenuController()
var customMenuItem = UIMenuItem(title: "Lookup", action: "lookupWord")
menuController.menuItems?.append(customMenuItem)
Thanks for any help you folks can give me. ;-)
Nevermind:
let mnuController = UIMenuController.sharedMenuController()
let lookupMenu = UIMenuItem(title: "Tshawb", action: "tshawb")
mnuController.menuItems = NSArray(array: [lookupMenu])
// This makes the menu item visible.
mnuController.setMenuVisible(true, animated: true)

Selectable UILabel contents

I have a list of URL addresses in a iOS Label object. It does not appear that the user can select and copy an item from the list and paste it into their Safari browser on the iOS device. Is there a way to accomplish this?
This feature is not there in UILabel.
You need to use UITextField or UITextView . Also do not forget to change its appearance and using
[... setEditable:NO];
It is actually possible to do with an UILabel, only you'll have to do some subclassing.
End result:
when your user long presses the label, he or she will see a copy-balloon.
Here are the steps to allow for making a label copy-able (as I can recall):
subclass UILabel
set userInteractionEnabled = YES
override canBecomeFirstResponder and return true
add a UILongPressGestureRecognizer
become first responder & present UIMenuController
Swift 3:
let menu = UIMenuController.shared
if !menu.isMenuVisible {
self.becomeFirstResponder()
menu.setTargetRect(self.bounds, in: self)
menu.setMenuVisible(true, animated: true)
}
override canPerformAction to allow copy
Swift 3:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return action == #selector(UIResponderStandardEditActions.copy(_:))
}
override copy method, UIPasteboard the text & hide UIMenuController
Swift 3:
let menu = UIMenuController.shared
let labelText = self.text ?? self.attributedText?.string
if let uLabelText = labelText {
let clipBoard = UIPasteboard.general
clipBoard.string = uText
}
menu.setMenuVisible(false, animated: true)
Here is swift 5 version of JoriDor's solution;
class CopyableLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
sharedInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
sharedInit()
}
func sharedInit() {
isUserInteractionEnabled = true
addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(showMenu(sender:))))
}
#objc
func showMenu(sender: AnyObject?) {
becomeFirstResponder()
let menu = UIMenuController.shared
if !menu.isMenuVisible {
menu.setTargetRect(bounds, in: self)
menu.setMenuVisible(true, animated: true)
}
}
override var canBecomeFirstResponder: Bool {
return true
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.copy(_:)) {
return true
}
return false
}
override func copy(_ sender: Any?) {
let board = UIPasteboard.general
board.string = text
let menu = UIMenuController.shared
menu.setMenuVisible(false, animated: true)
}
}

Resources