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;
Related
I am currently trying to prevent all UIResponderStandardEditActions like copy, paste, delete from showing up when the UITextfield is empty. I would only like to show them if the user has types a message. I have tried 2 solutions and currently don't work, I'm not sure if its to do with iOS 12 or. I have tried overriding the canPerformAction method both in a UITextfield extension and using a custom class later assigned to the UITextfield in the Storyboard but no luck. Is there another way to do this. Here is what I have tried.
extension UITextField {
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if self.text!.isEmpty {
return false
}
return action == #selector(UIResponderStandardEditActions.paste(_:))
}
}
class CustomTextField: UITextField {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.paste(_:)) || action == #selector(UIResponderStandardEditActions.copy(_:)) || action == #selector(UIResponderStandardEditActions.delete(_:)) {
return false
}
return true
}
}
Just override with your subclass and use this class instead of UITextField. By the way, this will disable copy paste,cut for whole conditions so that you need to add some hasText: Bool or related condition to the switch case.
#IBDesignable
class ActionsDisabledUITextField: UITextField {
#IBInspectable var isPasteEnabled: Bool = false
#IBInspectable var isSelectEnabled: Bool = false
#IBInspectable var isSelectAllEnabled: Bool = false
#IBInspectable var isCopyEnabled: Bool = false
#IBInspectable var isCutEnabled: Bool = false
#IBInspectable var isDeleteEnabled: Bool = false
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(UIResponderStandardEditActions.paste(_:)) where !isPasteEnabled,
#selector(UIResponderStandardEditActions.select(_:)) where !isSelectEnabled,
#selector(UIResponderStandardEditActions.selectAll(_:)) where !isSelectAllEnabled,
#selector(UIResponderStandardEditActions.copy(_:)) where !isCopyEnabled,
#selector(UIResponderStandardEditActions.cut(_:)) where !isCutEnabled,
#selector(UIResponderStandardEditActions.delete(_:)) where !isDeleteEnabled:
return false
default:
//return true : this is not correct
return super.canPerformAction(action, withSender: sender)
}
}
}
Here's what my textView looks like right now. It is a textview inside a scrollview.
I am trying to replace the usual UIMenuController menu items with Save and Delete but not getting there. Can someone help me out?
Here's my code:
import UIKit
class DetailViewController: UIViewController, UIGestureRecognizerDelegate, {
var selectedStory : URL!
#IBOutlet weak var textView: UITextView!
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var textSlider: UISlider! {
didSet {
configureSlider()
}
}
override func viewDidLoad() {
super.viewDidLoad()
let storyText = try? String(contentsOf: selectedStory)
textView.text = storyText
textView.isUserInteractionEnabled = true
let longPressGR = UILongPressGestureRecognizer(target: self, action: #selector(longPressHandler))
longPressGR.minimumPressDuration = 0.3 //
textView.addGestureRecognizer(longPressGR)
}
// MARK: - UIGestureRecognizer
#objc func longPressHandler(sender: UILongPressGestureRecognizer) {
guard sender.state == .began,
let senderView = sender.view,
let superView = sender.view?.superview
else { return }
senderView.becomeFirstResponder()
UIMenuController.shared.setTargetRect(senderView.frame, in: superView)
UIMenuController.shared.setMenuVisible(true, animated: true)
}
override var canBecomeFirstResponder: Bool {
return true
}
}
extension UITextView{
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == Selector(("_copy:")) || action == Selector(("_share:"))
{
return true
} else {
return false
}
}
}
extension UIScrollView{
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == Selector(("_copy:")) || action == Selector(("_share:"))
{
return true
} else {
return false
}
}
}
I'm getting 2 issues:
When I tap the screen, only the Share is showing up and the Copy is not.
The Share button shows up randomly near the center, not on the text that is selected, like so.
First of all, remove UITextView that is inside UIScrollView because UIScrollView itself is the parent class of UITextView. It will place the UIMenuController at appropriate frame.
Remove longPressGR and longPressHandler methods.
Replace this method,
extension UITextView{
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action.description == "copy:" || action.description == "_share:" {
return true
} else {
return false
}
}
}
You will get following output.
I'm trying to remove the items Look Up & Share... from the UIMenuController. How would I specifically remove the two and keep my custom one. Here is what I've achieved so far:
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: "My Button", action: #selector(myButtonSel))
UIMenuController.shared.menuItems = [menuItem1]
}
Here is the canPerformAction I have:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
//let shareSelector: Selector = NSSelectorFromString("_share:")
if webView?.superview != nil {
if action == #selector(myButtonSel){
return true
}
}
return super.canPerformAction(action, withSender: sender)
}
Also for some odd reason, when I try to remove all the default items and keep only my custom, it does not work. Here is the code I attempted for that:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
//let shareSelector: Selector = NSSelectorFromString("_share:")
if webView?.superview != nil {
if action == #selector(myButtonSel){
return true
}
else {
return false
}
}
return super.canPerformAction(action, withSender: sender)
}
Even when I try to remove all of the other items and keep my custom, I'm not able to do so. All I'm able to do is add my custom item.
I tried this but it worked for my by subclassing the WebView and overriding canPerformAction method, inside which I manually removed the default options.
override func canPerformAction(_ action: Selector, withSender sender: AnyObject?) -> Bool {
if action == #selector(cut(_:)) {
return false
}
if action == #selector(paste(_:)) {
return false
}
if action == #selector(select(_:)) {
return false
}
if action == #selector(selectAll(_:)) {
return false
}
...
return super.canPerformAction(action, withSender: sender)
}
I referred to this answer by Ike10 and it had worked for me. Give it a shot.
Objective C version of removing default items from UIMenuController. The default items are part of UIResponderStandardEditActions! For removing default items make sure to create a subclasss of UITextField or UITextView for the functionality to work otherwise in UiViewController class it will not work.
#import "CustomTextField.h"
#implementation CustomTextField
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(captureTextFromCamera:) ||
action == #selector(delete:) ||
action == #selector(cut:) ||
[NSStringFromSelector(action) isEqualToString:#"_promptForReplace:"] ||
[NSStringFromSelector(action) isEqualToString:#"_transliterateChinese:"] ||
[NSStringFromSelector(action) isEqualToString:#"_insertDrawing:"] ||
[NSStringFromSelector(action) isEqualToString:#"_lookup:"] ||
[NSStringFromSelector(action) isEqualToString:#"_define:"] ||
[NSStringFromSelector(action) isEqualToString:#"_translate:"] ||
[NSStringFromSelector(action) isEqualToString:#"_addShortcut:"] ||
[NSStringFromSelector(action) isEqualToString:#"_accessibilitySpeak:"] ||
[NSStringFromSelector(action) isEqualToString:#"_accessibilitySpeakLanguageSelection:"] ||
[NSStringFromSelector(action) isEqualToString:#"_share:"] )
{
return false;
}
NSLog(#"OPtion :- %#",NSStringFromSelector(action));
return [super canPerformAction:action withSender:sender];
}
#end
The default available options are below which you want to disable.
cut:
copy:
paste:
delete:
_promptForReplace:
_transliterateChinese:
_insertDrawing:
captureTextFromCamera:
_showTextStyleOptions:
_lookup:
_define:
_translate:
_addShortcut:
_accessibilitySpeak:
_accessibilitySpeakLanguageSelection:
_accessibilityPauseSpeaking:
_share:
makeTextWritingDirectionRightToLeft:
makeTextWritingDirectionLeftToRight:
For swift users please convert it . It is easy to convert.
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.
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.
:):):)