113

The UITextView's Copy, Cut, Select, Select All functionality is shown by default when I press down on the screen. But, in my project the UITextField is only read only. I do not require this functionality. Please tell me how to disable this feature.

Aishwarya
  • 1,587
  • 3
  • 14
  • 9
  • 3
    place [UIMenuController sharedMenuController].menuVisible = NO; in - (BOOL) canPerformAction:(SEL)action withSender:(id)sender method. – ravoorinandan Jul 01 '11 at 09:51

33 Answers33

111

The easiest way to disable pasteboard operations is to create a subclass of UITextView that overrides the canPerformAction:withSender: method to return NO for actions that you don't want to allow:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
    if (action == @selector(paste:))
        return NO;
    return [super canPerformAction:action withSender:sender];
}

Also see UIResponder

Pang
  • 9,564
  • 146
  • 81
  • 122
rpetrich
  • 32,196
  • 6
  • 66
  • 89
  • 1
    @rpetrichm, I used you solution, copy/paste/cut/select/selectAll options are disabled but there's still coming Replace...|BIU|Define options. I want to disable that complete menu. – Bhushan B May 08 '14 at 10:38
  • 4
    `- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; }` - to block all of the options – Islam Jan 10 '15 at 18:09
  • That's great, however I don't know how to do this for UISearchBar - as there also is a text field inside I thought I could overwrite the method of a UISearchBar's subclass but that doesn't work unfortunately. Any ideas for this? – borchero Jan 12 '15 at 21:51
  • i have many controls. how to allow copy paste just on one control? – Gaucho May 24 '15 at 17:11
  • This is not working for UITextView, though I've seen this solution suggested in a number of places on SO. Has anyone figured out how to do this? – Pigpocket Jun 24 '18 at 22:16
  • @Pigpocket hi buddy, did u find other solution? same problem here – famfamfam Feb 20 '21 at 16:40
68

Subclass UITextView and overwrite canBecomeFirstResponder:

- (BOOL)canBecomeFirstResponder {
    return NO;
}

Note, that this only applies for non-editable UITextViews! Haven't tested it on editable ones...

iCoder
  • 713
  • 5
  • 2
  • I think `return NO;` on `- (BOOL)canPerformAction:(SEL)action withSender:(id)sender` method is a better option. – Islam Jan 10 '15 at 18:10
31

This was the best working solution for me:

UIView *overlay = [[UIView alloc] init];  
[overlay setFrame:CGRectMake(0, 0, myTextView.contentSize.width, myTextView.contentSize.height)];  
[myTextView addSubview:overlay];  
[overlay release];

from: https://stackoverflow.com/a/5704584/1293949

Community
  • 1
  • 1
Saraiva Alcides
  • 343
  • 3
  • 2
28

If you want to disable cut/copy/paste on all UITextView of your application you can use a category with :

@implementation UITextView (DisableCopyPaste)

- (BOOL)canBecomeFirstResponder
{
    return NO;
}

@end

It saves a subclassing... :-)

Damien Debin
  • 2,812
  • 25
  • 41
  • 3
    you can also put this only in your /, file where you need this behaviour. – markus_p Apr 03 '12 at 10:07
  • 4
    This only works as a side-effect and prevents the `UITextView` from behaving as expected when, for example, it is editable and gets a touch. It is much to override `canPerformAction:withSender:`; that's what the protocol is for. – jdc Mar 06 '13 at 23:09
  • Hello, i need to know: what you mean by subclassing ?? – Mutawe Apr 17 '13 at 12:38
  • 16
    You cannot safely override a method using a category. This is undefined behavior. You must subclass in order to safely override a method. – Rob Napier Apr 22 '13 at 18:42
  • 2
    Note: This will apply to all UITextViews in your application. Not ideal most of the time. – bbrame Aug 22 '13 at 17:10
  • 2
    Also note that Apple [advises against this](https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html#//apple_ref/doc/uid/TP40011210-CH6-SW4): "If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime ... [and] can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes." – Rob Jul 28 '15 at 19:59
  • This is dangerous: if any dependency or even Apple code you trigger presents a `UITextView` you're breaking the behavior of that third party component. – mxcl Sep 05 '17 at 04:55
27

@rpetrich answer worked for me. I'm posting the expanded code in case it saves someone some time.

In my case I want no popup whatsoever, but I do want the UITextField to be able to become first responder.

Unfortunately, you still get the magnifier popup when you tap and hold the textfield.

@interface NoSelectTextField : UITextField

@end

@implementation NoSelectTextField

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    if (action == @selector(paste:) ||
        action == @selector(cut:) ||
        action == @selector(copy:) ||
        action == @selector(select:) ||
        action == @selector(selectAll:) ||
        action == @selector(delete:) ||
        action == @selector(makeTextWritingDirectionLeftToRight:) ||
        action == @selector(makeTextWritingDirectionRightToLeft:) ||
        action == @selector(toggleBoldface:) ||
        action == @selector(toggleItalics:) ||
        action == @selector(toggleUnderline:)
        ) {
            return NO;
    }
    return [super canPerformAction:action withSender:sender];
}

@end

Swift 4

class NoSelectTextField: UITextField {

    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(paste(_:)) ||
            action == #selector(cut(_:)) ||
            action == #selector(copy(_:)) ||
            action == #selector(select(_:)) ||
            action == #selector(selectAll(_:)) ||
            action == #selector(delete(_:)) ||
            action == #selector(makeTextWritingDirectionLeftToRight(_:)) ||
            action == #selector(makeTextWritingDirectionRightToLeft(_:)) ||
            action == #selector(toggleBoldface(_:)) ||
            action == #selector(toggleItalics(_:)) ||
            action == #selector(toggleUnderline(_:)) {
            return false
        }
        return super.canPerformAction(action, withSender: sender)
    }

}
Devin Pitcher
  • 2,562
  • 1
  • 18
  • 11
20

If you don't need UITextView to scroll, then the simplest solution that doesn't involve sub-classing is to simply disable user interaction for the text view:

textField.userInteractionEnabled = NO;
Luke Redpath
  • 10,474
  • 3
  • 27
  • 26
  • 2
    This takes away tapping on links etc. if that is what is in the textview. Should be noted that this is not a good solution for wanting to hide the select/copy/paste, but also keep some level of interaction enabled. – barfoon Feb 09 '12 at 03:47
  • 3
    Well, I would have thought that would be obvious. – Luke Redpath Feb 10 '12 at 19:47
  • I'm using text fields as labels for pictures in a game and I don't want the magnifying glass to appear and there is no reason to copy the text. This works great for me. I'm using styled text in a UIWebView and the same line works there as well. – JScarry Feb 17 '13 at 18:48
15

The easiest way is to create a subclass of UITextView that overrides the canPerformAction:withSender:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender    
{    
     [UIMenuController sharedMenuController].menuVisible = NO;  //do not display the menu
     [self resignFirstResponder];                      //do not allow the user to selected anything
     return NO;
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
haiLong
  • 1,116
  • 10
  • 11
  • This is the best solution for being able to type text but nothing else, and allows taps on the text-field to no longer to 'intercepted'. This does what the OP wanted, and more. – hlfcoding Mar 05 '13 at 04:16
13

When I return NO in the canPerformAction on iOS 7 I will get a lot of errors like this:

<Error>: CGContextSetFillColorWithColor: invalid context 0x0. This is a serious error. This application, or a library it uses, is using an invalid context and is thereby contributing to an overall degradation of system stability and reliability. This notice is a courtesy: please fix this problem. It will become a fatal error in an upcoming update.

My solution is the following:

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        [[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO];
    }];
    return [super canPerformAction:action withSender:sender];
}

The trick is to hide the menu controller in the next cycle on the main queue (just after it is displayed).

Adam Wallner
  • 2,292
  • 23
  • 20
  • really nice. the only problem is that i have 2 textviews and one textField and i want to avoid the copy-paste only on the textView fields. how to identity who called the canPerformAction? the sender variable is the UIMenuController – Gaucho May 24 '15 at 17:05
  • 1
    It works on the subclass of UITextField (or UITextView). If you don't use the subclass you made, it won't have any effect. E.g. I created a TextFieldWithoutCopyPaste and used that where I didn't want to have copy paste functionality. – Adam Wallner May 25 '15 at 08:33
  • ok, you're right. But in my case, the textView needs subclass to use the canPerformAction and the textField needs subclass to use the textFieldDidBeginEditing in order to animate the window when the keyboard is shown. the animation moves the window with the keyboard. I use this method to avoid that the keyboard covers the textField. – Gaucho May 25 '15 at 16:33
9

This is the easiest way to disable the entire Select/Copy/Paste Menu in a UITextView

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{    
    [UIMenuController sharedMenuController].menuVisible = NO;
    return NO;    
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
GL777
  • 245
  • 4
  • 11
5

If you are looking to replace the keyboard with a, let's say, UIPicker as the inputView (with of course a toolbar as an inputAccesotyView), then this workaround might help...

  • Implement textFieldShouldBeginEditing:
  • inside put textField.userInteractionEnabled = NO;
  • Then when you are about to close the UIPickerView, set it to YES.

By doing this, you'd be able to tap on the UITextField and show the options to choose from the UIPickerView, at this time your UITextField would, indeed, not react to any touch event (this includes touch and hold for cut, copy and paste). However, you would have to remember to set it back to YES when you are closing your UIPickerView however you won't be able to access your UIPickerView again.

The only moment when it fails is when the user starts by tapping and holding the UITextView, then you'd see cut copy and paste again for the first time. This is why you should always validate your inputs. This is the easiest I can think of. The other option was to use a UILabel for read-only text but you miss a lot of great functionality from UITextView.

Dharmesh Dhorajiya
  • 3,976
  • 9
  • 30
  • 39
rn3sto
  • 99
  • 1
  • 4
5

You can fix this in your storyboard by uncheck these boxes:

enter image description here

Or you can set programatically like so:

textView.selectable = false
textView.editable = false
Twitter khuong291
  • 11,328
  • 15
  • 80
  • 116
5

Subclass UITextView - swift 4.0

     override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return false
    }
Manee ios
  • 1,112
  • 11
  • 14
5

If you want to disable popup for UITextField then try this UITextFieldDelegate method to toggle isUserInteractionEnabled.

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    textField.isUserInteractionEnabled = false
    return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
    textField.isUserInteractionEnabled = true
    return true
}
Jayesh Thanki
  • 2,037
  • 2
  • 23
  • 32
  • I don't like sub-classing and overriding functions for such small tasks as it just creates more clutter, so this delegate approach works well for me. –  Mar 19 '21 at 13:50
  • This works really great for me, exactly what I was looking for! – Volodymyr Davydenko Oct 14 '21 at 14:46
4

Since iOS 7 there is a property on UITextView:

 @property(nonatomic,getter=isSelectable) BOOL selectable;

This keeps a view from allowing text selections. Works great for me.

Epaga
  • 38,231
  • 58
  • 157
  • 245
3

This worked for me. Make sure you are calling resignFirstResponder on the textView

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
  [self.textView resignFirstResponder];
  return NO;
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
verma
  • 753
  • 8
  • 8
3

This can be done easily in storyboard (Xcode 6). Just uncheck Editable and Selectable in Attributes Inspector. You can still scroll the text view.enter image description here

Hari Kunwar
  • 1,661
  • 14
  • 10
2

I have done it. On my UITextView I have disabled cut, copy, select, etc. option very easily.

I placed a UIView at the same place where i had placed the UITextView, but on self.view and added a touchDelegate method as follows:

(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
{
    UITouch *scrollTouch=[touches anyObject];
    if(scrollTouch.view.tag==1)
    {
        NSLog(@"viewTouched");
        if(scrollTouch.tapCount==1)
            [textView1 becomeFirstResponder];
        else if(scrollTouch.tapCount==2)
        {
            NSLog(@"double touch");
            return;
        }

    }
}

and it worked for me. Thank you.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
2

I provided a working answer here to disable text selection + magnifier, keeping enabled clikable links Hope that helps :

After quite a long time trying, I managed to stop text selection, magnifying, and keeping data detection (links clickable etc) by overriding addGestureRecognizer on a UITextView subclass allowing only UILongPressGestureRecognizer delaying touch ending:

UIUnselectableTextView.m

-(void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
    if([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] && gestureRecognizer.delaysTouchesEnded)
    {
        [super addGestureRecognizer:gestureRecognizer];
    }
}
Community
  • 1
  • 1
Thibaud David
  • 496
  • 2
  • 11
2

For Swift 3 it's changed to:

override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return false
}
Andrey Gordeev
  • 30,606
  • 13
  • 135
  • 162
1

Swift

textView.selectable = false // disable text selection (and thus copy/paste/etc)

Related

textView.editable = false // text cannot be changed but can still be selected and copied
textView.userInteractionEnabled = false // disables all interaction, including scrolling, clicking on links, etc.
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
1

If you want to add a custom option to your UITextView but disable the existing functions this is how you do it on Swift 3 :

To disable the copy, paste, cut funcionality create a subclass and override the following:

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

On the ViewController you have your CustomTextView add the following to add your options:

  let selectText = UIMenuItem(title: "Select", action: #selector(ViewController.selected))

    func selected() {

    if let selectedRange = textView.selectedTextRange, let 
     selectedText = textView.text(in: selectedRange) {

     }


    print("User selected text: \(selectedText)")

    }
Sam Bing
  • 2,794
  • 1
  • 19
  • 29
1

This method will completely disable the Select, Select All, Paste menu. If you still get some other action, then just add that to the if condition as below.

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender // This is to disable select / copy / select all / paste menu
    {
        if (action == @selector(copy:) || action == @selector(selectAll:) || action == @selector(select:) || action == @selector(paste:))
            return NO;
        return [super canPerformAction:action withSender:sender];
    }
1

UITextView has two property that will do what you need: isSelectable and isEditable.

Setting isEditable to false you will avoid the user to edit the text and setting isSelectable to false you will avoid the user to select text inside your textView so this will prevent the action menu to be shown.

L. Davì
  • 31
  • 2
1

If you are looking for a iOS >=13.0 version you can simply use this extension at any level of implementation down to the UIResponder:

extension UITextField {
  override var editingInteractionConfiguration: UIEditingInteractionConfiguration {
      return .none
  }
}
zero3nna
  • 2,770
  • 30
  • 28
0

You can just create category like this:

UITextView+Selectable.h

@interface UITextView (Selectable)

@property (nonatomic, assign, getter = isTextSelectable) bool textSelectable;

@end

UITextView+Selectable.m

#import "UITextView+Selectable.h"

#import <objc/runtime.h>

#define TEXT_SELECTABLE_PROPERTY_KEY @"textSelectablePropertyKey"

@implementation UITextView (Selectable)

@dynamic textSelectable;

-(void)setTextSelectable:(bool)textSelectable {
    objc_setAssociatedObject(self, TEXT_SELECTABLE_PROPERTY_KEY, [NSNumber numberWithBool:textSelectable], OBJC_ASSOCIATION_ASSIGN);
}

-(bool)isTextSelectable {
    return [objc_getAssociatedObject(self, TEXT_SELECTABLE_PROPERTY_KEY) boolValue];
}

-(bool)canBecomeFirstResponder {
    return [self isTextSelectable];
}

@end
  • 1
    This isn't a good way of solving it. Firstly it affects everything, as it's in the category. Secondly, using associated objects for rather easy task may case more troubles later with debugging (why does it work like this when you forget what you did) than give profit. It's good to avoid it whenever possible in my opinion. Makes code less easy to find&debug and understand by new programmer in the project. – Nat Sep 15 '14 at 11:15
0

Subclassing UITextView and overriding - (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer is another possibility to disable unwanted actions.

Use the class of the gestureRecognizer-object to decide if the action should be added or not.

RhodanV5500
  • 1,087
  • 12
  • 16
0

(SWIFT) If you want just a basic text field with none of the menu options or magnifying glass then create a subclass of UITextField returning false to gestureRecognizerShouldBegin:

class TextFieldBasic: UITextField {
    override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {

        return false
    }
}

This will bypass all the touch functionality on the text field but still allow you to use the popup keyboard to add / remove characters.

If you are using storyboard just assign the newly created class to the text field or if you are creating a text field programatically:

var basicTextField = TextFieldBasic()
basic = basicTextField(frame: CGRectMake(10, 100, 100,35))
basic.backgroundColor = UIColor.redColor()
self.view.addSubview(basic)

basic.becomeFirstResponder()
Krivvenz
  • 3,911
  • 3
  • 22
  • 32
0
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool 
{
    NSOperationQueue .mainQueue().addOperationWithBlock({ () -> Void in   

        [UIMenuController .sharedMenuController() .setMenuVisible(false, animated: true)]

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

Swift 3

In order to do this, you need to subclass your UITextView and put this method.

override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if (action == #selector(copy(_:))) {
            return false
        }

        if (action == #selector(cut(_:))) {
            return false
        }

        if (action == #selector(paste(_:))) {
            return false
        }

        return super.canPerformAction(action, withSender: sender)
    }
Zaldy Bughaw
  • 797
  • 8
  • 16
0

Please find the sample code for reference:

 override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(copy(_:)) || action == #selector(paste(_:)) || action == #selector(UIResponderStandardEditActions.paste(_:)) ||
            action == #selector(replace(_:withText:)) ||
            action == #selector(UIResponderStandardEditActions.cut(_:)) ||
        action == #selector(UIResponderStandardEditActions.select(_:)) ||
        action == #selector(UIResponderStandardEditActions.selectAll(_:)) ||
        action == #selector(UIResponderStandardEditActions.delete(_:)) ||
        action == #selector(UIResponderStandardEditActions.makeTextWritingDirectionLeftToRight(_:)) ||
        action == #selector(UIResponderStandardEditActions.makeTextWritingDirectionRightToLeft(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleBoldface(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleItalics(_:)) ||
        action == #selector(UIResponderStandardEditActions.toggleUnderline(_:)) ||
        action == #selector(UIResponderStandardEditActions.increaseSize(_:)) ||
        action == #selector(UIResponderStandardEditActions.decreaseSize(_:))

       {
            return false
        }

        return true
    }
mail2subhajit
  • 1,106
  • 5
  • 16
0

You can use method swizzling for acheiving this. Method swizzling is to be performed for the method : func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool

If you have multiple textFeild/textView throughout app, in different storyboards and if you want all of them to discard clipboard options at once then this is best solution.Method swizzling implementation can be added in the textFeild/textView extensions. Swizzle function can be called in didginish launch in app delegate.

0

I solved the problem of my picker in text field. To remove selection. Enough "CGRect.null"

override func caretRect(for position: UITextPosition) -> CGRect {
    return CGRect.null
}
override func selectionRects(for range: UITextRange) -> [UITextSelectionRect] {
    return []
}
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return false
}
zef_s
  • 11
  • 2
-1

Use func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { retrun bool } in place of textFieldShouldBeginEditing.

class ViewController: UIViewController , UITextFieldDelegate {

    @IBOutlet weak var textField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        //Show date picker
        let datePicker = UIDatePicker()
        datePicker.datePickerMode = UIDatePickerMode.date
        textField.tag = 1
        textField.inputView = datePicker
    }

    func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
        if textField.tag == 1 {
            textField.text = ""
            return false
        }

        return true
    }

    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        if textField.tag == 1 {
            textField.text = ""
            return false
        }

        return true
    }
}

Create a new class with name StopPasteAction.swift

import UIKit

class StopPasteAction: UITextField {

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

Add the class new class with you current TextField

enter image description here

Anh Pham
  • 2,108
  • 9
  • 18
  • 29
anson
  • 1,436
  • 14
  • 16