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 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;
I have outlets to 3 textfields in my code as follows :
#IBOutlet var oldPasswordTextField: UITextField! { didSet { oldPasswordTextField.delegate = self } }
#IBOutlet var newPasswordTextField: UITextField! { didSet { newPasswordTextField.delegate = self } }
#IBOutlet var confirmPasswordTextField: UITextField! { didSet { confirmPasswordTextField.delegate = self } }
In my textFieldShouldReturn delegate method, I want to make the next textfield as the first responder on click of return as follows :
func textFieldShouldReturn(textField: UITextField) -> Bool { //delegate method
textField.resignFirstResponder()
if textField == oldPasswordTextField
{
newPasswordTextField.becomeFirstResponder()
}
else if textField == newPasswordTextField
{
confirmPasswordTextField.becomeFirstResponder()
}
else
{
saveButtonTapped()
}
return true
}
But it wasn't working. So I tried to check what was going wrong. So I tried this :
func textFieldShouldReturn(textField: UITextField) -> Bool { //delegate method
if textField.canResignFirstResponder()
{
print("textField can resign first responder")
if textField.resignFirstResponder() == true
{
print("textField resigned first responder")
}
else
{
print("textField didnt resign first responder")
}
}
else
{
print("textField cant resign first responder")
}
if textField == oldPasswordTextField
{
if newPasswordTextField.canBecomeFirstResponder() == true
{
if newPasswordTextField.becomeFirstResponder() == true
{
print("newPassword field has become first responder")
}
else
{
print("newPassword field cannot become first responder")
}
}
}
else if textField == newPasswordTextField
{
confirmPasswordTextField.becomeFirstResponder()
}
else
{
saveButtonTapped()
}
return true
}
And overtime I get "textField cant resign first responder"
"newPassword field cannot become first responder"
on my console. I don't know whats going wrong here. Why are the canResignFirstResponder() method and becomeFirstResponder() methods returning false? Any help?
Try to use tags in your text fields. This worked for me:
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSInteger nextTag = textField.tag + 1;
// Try to find next responder
UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
if (nextResponder) {
// Found next responder, so set it.
[nextResponder becomeFirstResponder];
} else {
// Not found, so remove keyboard.
[textField resignFirstResponder];
}
return NO; // We do not want UITextField to insert line-breaks.
}
Sorry I didn't see it wasn't Objective-C, in swift it would be:
func textFieldShouldReturn(textField: UITextField!) -> Bool {
let nextTag:Int = textField.tag + 1
// Try to find next responder
if let nextResponder: UIResponder! = textField.superview!.viewWithTag(nextTag){
nextResponder.becomeFirstResponder()
} else {
// Not found, so remove keyboard.
textField.resignFirstResponder()
}
return false; // We do not want UITextField to insert line-breaks.
}
I found the solution here: Switching between Text fields on pressing return key in Swift
Got it. The above code is fine. The mistake in my code was that my 'textFieldShouldEndEditing' method was returning false which was why my resignFirstResponder method was returning false and it wasn't working.
So I made the 'textFieldShouldEndEditing' method return true and it works fine. :)
func textFieldShouldEndEditing(textField: UITextField) -> Bool { //delegate method
return true
}
I need to support pasting of images into a UITextView. With an image copied to the clipboard, the "Paste" option doesn't seem to pop up. It does when there's text on the clipboard.
This is how to override the paste option in a custom UITextView. But I need help on how to get the option to show up to begin with...
// This gets called when user presses menu "Paste" option
- (void)paste:(id)sender{
UIImage *image = [UIPasteboard generalPasteboard].image;
if (image) {
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
textAttachment.image = image;
NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:textAttachment];
self.attributedText = imageString;
} else {
// Call the normal paste action
[super paste:sender];
}
}
I came across a few related questions, but they weren't helpful for an inexperienced developer like myself:
How to get UIMenuController work for a custom view?, How to paste image from pasteboard on UITextView?
I answered my own question. All you have to do is have the UITextView say "I can receive pasted images" by overriding this UITextView method:
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(paste:) && [UIPasteboard generalPasteboard].image)
return YES;
else
return [super canPerformAction:action withSender:sender];
}
You're welcome.
Thanks #Matt your answer helped me. Just extending your answer which may help some one,
Subclassing UITextview, which shows paste option on long press when you have image in pasteboard.
class MyTextView:UITextView {
var onPasteImage:(()->Void)?
override func awakeFromNib() {
super.awakeFromNib()
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(paste(_:)) && UIPasteboard.general.image != nil {
return true
}else{
return super.canPerformAction(action, withSender: sender)
}
}
override func paste(_ sender: Any?) {
super.paste(sender)
if UIPasteboard.general.image != nil {
onPasteImage?()
}
}
}
And wait for onPasteImage closure to be called on tap paste in textview,
inputFieldForReply.textView.onPasteImage = { [weak self] in
if let image = UIPasteboard.general.image {
// Process pasted image
}
}
I am making a registration alertview that has a UITextField in it where the user can enter their registration number. everything is pretty much their, however I would like to remove the copy paste function from the textfield programmatically since their is no InterfaceBuilder version of the textfield I have no idea how to do this..
here Is my UIalertview thus far...
- (void)pleaseRegisterDevice {
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:#"Please Register Device!" message:#"this gets covered" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
regTextField = [[UITextField alloc] initWithFrame:CGRectMake(12.0, 45.0, 260.0, 25.0)];
[regTextField setBackgroundColor:[UIColor whiteColor]];
regTextField.textAlignment = UITextAlignmentCenter;
[myAlertView addSubview:regTextField];
[myAlertView show];
[myAlertView release];
}
This post has many nice solutions: How disable Copy, Cut, Select, Select All in UITextView
My favourite is to override canPerformAction:withSender::
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(paste:))
return NO;
return [super canPerformAction:action withSender:sender];
}
Storyboard users may want to look at this solution, as long as you are ok with subclassing.
I don't think that there is an easy way to achieve this through extensions or protocols.
Swift 3.1
import UIKit
#IBDesignable
class CustomTextField: UITextField {
#IBInspectable var isPasteEnabled: Bool = true
#IBInspectable var isSelectEnabled: Bool = true
#IBInspectable var isSelectAllEnabled: Bool = true
#IBInspectable var isCopyEnabled: Bool = true
#IBInspectable var isCutEnabled: Bool = true
#IBInspectable var isDeleteEnabled: Bool = true
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)
}
}
}
Gist link
For iOS8.0+, Xcode 6.0.1, ARC enabled
Hoping to save a beginner, like myself, some time implementing this...
To implement disabling copy/paste/cut/etc. you must subclass UITextField and override...
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
To do this...
Create a new class that is a subclass of UITextField (i.e. a new .h and .m files to be included within your app folder). So File->New->"Cocoa Touch Class"->Next->"PasteOnlyUITextField" (for example), subclass of "UITextField"->Next->Create.
Once the .h and .m files are created for our new subclass of UITextField called "PasteOnlyUITextField"...
PasteOnlyUITextField.h
#import <UIKit/UIKit.h>
#interface PasteOnlyUITextField : UITextField
#end
PasteOnlyUITextField.m
#import "PasteOnlyUITextField.h"
#implementation PasteOnlyUITextField
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == #selector(paste:))
{
return true;
}
return false;
}
#end
Now make sure you import PasteOnlyUITextField.h where you are going to use it, e.g. YourUIViewController.h file...
#import "PasteOnlyUITextField.h"
Now you must use the subclass, either progrommatically or with identity inspector
PasteOnlyUITextField *pasteOnlyUITextField = [[PasteOnlyUITextField alloc] init...];
or...
Select the UITextField and go to the identity inspector, select its class.
You can change the logic associated with the menu options as you see fit...
Hope this helps! Thanks to all the original contributors.
I have found a way with swift using extension and associatedObject without subclassing.
I use a property readonly to disable paste/cut but this sample can be adapted.
Swift 3 updated as of 27/11/2016
var key: Void?
class UITextFieldAdditions: NSObject {
var readonly: Bool = false
}
extension UITextField {
var readonly: Bool {
get {
return self.getAdditions().readonly
} set {
self.getAdditions().readonly = newValue
}
}
private func getAdditions() -> UITextFieldAdditions {
var additions = objc_getAssociatedObject(self, &key) as? UITextFieldAdditions
if additions == nil {
additions = UITextFieldAdditions()
objc_setAssociatedObject(self, &key, additions!, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
return additions!
}
open override func target(forAction action: Selector, withSender sender: Any?) -> Any? {
if ((action == #selector(UIResponderStandardEditActions.paste(_:)) || (action == #selector(UIResponderStandardEditActions.cut(_:)))) && self.readonly) {
return nil
}
return super.target(forAction: action, withSender: sender)
}
}
Other Swift (2.2)
import UIKit
var key: Void?
class UITextFieldAdditions: NSObject {
var readonly: Bool = false
}
extension UITextField {
var readonly: Bool {
get {
return self.getAdditions().readonly
}
set {
self.getAdditions().readonly = newValue
}
}
private func getAdditions() -> UITextFieldAdditions {
var additions = objc_getAssociatedObject(self, &key) as? UITextFieldAdditions
if additions == nil {
additions = UITextFieldAdditions()
objc_setAssociatedObject(self, &key, additions!, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
return additions!
}
public override func targetForAction(action: Selector, withSender sender: AnyObject?) -> AnyObject? {
if ((action == Selector("paste:") || (action == Selector("cut:"))) && self.readonly) {
return nil
}
return super.targetForAction(action, withSender: sender)
}
}
Implement this Method in ViewController.m This Method will help you to disable Options on UITextField.
It Includes paste, select, selectAll and copy option on your Corresponding UITextField.
This method is very useful in case of UITextField when you want to take this for Password or DateOfBirth or whatever you want.
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if ((_TextField1 isFirstResponder] || [_TextFied2 isFirstResponder]) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
}];
}
return [super canPerformAction:action withSender:sender];
}
In Swift, If you want your text field to disable all UIResponderStandardEditActions (cut, copy, paste, look up, share, select), use this in UITextFieldDelegate.
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
textField.isUserInteractionEnabled = false
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
textField.isUserInteractionEnabled = true
}
In iOS 9 we can hide the copy paste bar from keyboard
-(void) customMethod{
yourTextField.inputAssistantItem.leadingBarButtonGroups = #[];
yourTextField.inputAssistantItem.trailingBarButtonGroups = #[];
}
Swift 5 solution:
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if action == #selector(UIResponderStandardEditActions.copy(_:)) || action == #selector(UIResponderStandardEditActions.paste(_:)) {
return false
}
return true
}
Small update of this answer for iOS 10 and earlier (Swift 3):
open override func target(forAction action: Selector, withSender sender: Any?) -> Any? {
guard isReadonly else {
return super.target(forAction: action, withSender: sender)
}
if #available(iOS 10, *) {
if action == #selector(UIResponderStandardEditActions.paste(_:)) {
return nil
}
} else {
if action == #selector(paste(_:)) {
return nil
}
}
return super.target(forAction: action, withSender: sender)
}
Try this in your viewController
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
}];
return [super canPerformAction:action withSender:sender];
}
Disable all actions by UITextField subclass.
import UIKit
class CustomTextField: UITextField {
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
you can extension textview or textfield in swift, like this:
extension UITextView {
open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
}
If disabled text selection works for you, try this.
class NoMoreSelectionTextField: UITextField {
override func caretRect(for position: UITextPosition) -> CGRect {
return CGRect.zero
}
override var selectedTextRange: UITextRange? {
get { return nil }
set { return }
}
}
Overriding targetForAction:withSender is best IMHO:
- (id)targetForAction:(SEL)action withSender:(id)sender
{
if (action == #selector(paste:)) {
return nil;
}
return [super targetForAction:action withSender:sender];
}
Swift 3.0 version
class NoMenuTextField: UITextField {
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
if (action == #selector(NSObject.paste(_:))) {
return false
}
return super.canPerformAction(action, withSender: sender)
}
}
use for iOS 7 or later
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
}];
return [super canPerformAction:action withSender:sender];
}
Just set userInteractionEnabled = NO;