如何在UITextView中禁用复制,剪切,选择,全选


110

UITextView的复制,剪切,选择,全选功能在默认情况下,当我按下屏幕上显示。但是,在我的项目中,这UITextField是只读的。我不需要此功能。请告诉我如何禁用此功能。


3
放置[UIMenuController sharedMenuController] .menuVisible = NO; --(BOOL)canPerformAction:(SEL)withSender:(id)sender方法中的动作。
ravoorinandan 2011年

Answers:


108

禁用粘贴板操作的最简单方法是创建一个子类,UITextView该子类重写该canPerformAction:withSender:方法以返回NO您不想执行的操作:

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

另请参见UIResponder


1
@rpetrichm,我使用了您的解决方案,复制/粘贴/剪切/选择/选择所有选项都被禁用,但是仍然有替换... | BIU |定义选项。我想禁用该完整菜单。
Ameet Dhas 2014年

3
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; }-封锁所有选项
Islam Q.

太好了,但是我不知道如何为UISearchBar做到这一点-因为里面还存在一个文本字段,我认为我可以覆盖UISearchBar的子类的方法,但是不幸的是,它无法正常工作。有什么想法吗?
borchero 2015年

我有很多控件。如何只允许在一个控件上粘贴复制?
高乔2015年

尽管我已经在SO上的许多地方看到了这种解决方案的建议,但这不适用于UITextView。有谁知道如何做到这一点?
Pigpocket

67

子类UITextView并覆盖canBecomeFirstResponder:

- (BOOL)canBecomeFirstResponder {
    return NO;
}

注意,这仅适用于不可编辑的UITextViews!尚未在可编辑的文件上对其进行测试...


我认为,return NO;- (BOOL)canPerformAction:(SEL)action withSender:(id)sender方法是一个更好的选择。
Islam Q. 2015年

29

如果要在所有 UITextView应用程序上禁用剪切/复制/粘贴功能,则可以使用带有以下类别的类别

@implementation UITextView (DisableCopyPaste)

- (BOOL)canBecomeFirstResponder
{
    return NO;
}

@end

它保存一个子类... :-)


3
您也可以仅将其放在需要此行为的/,文件中。
markus_p

4
这仅是一种副作用UITextView,例如在可编辑和可触摸的情况下,无法按预期表现。要压倒太多canPerformAction:withSender:; 这就是协议的目的。
jdc

16
您不能安全地使用类别覆盖方法。这是未定义的行为。您必须子类化才能安全地覆盖方法。
Rob Napier 2013年

2
注意:这将应用于您的应用程序中的所有UITextViews。大多数时候都不理想。
bbrame

2
另请注意,Apple 建议不要这样做:“如果在类别中声明的方法的名称与原始类中的方法的名称相同,或者在同一类别(甚至是超类)上的另一个类别中的方法的名称相同,则行为是对于在运行时使用哪种方法实现未定义... [和]可能会在使用类别向标准Cocoa或Cocoa Touch类添加方法时引起问题。”
罗布(Rob)2015年

29

这是最适合我的解决方案:

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

来自:https://stackoverflow.com/a/5704584/1293949


3
不错,这里没有子类!
Martin Reichl

7
我喜欢这里的胶带和打包线方法。希望我能再给您+1 :)相反,这里有个金星:i.imgur.com/EXLFt1Z.jpg
jdc

22

@rpetrich的答案对我有用。我正在发布扩展代码,以防它节省一些时间。

就我而言,我不希望弹出窗口,但我确实希望UITextField能够成为第一响应者。

不幸的是,当您按住文本框时,仍然会弹出放大镜。

@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

斯威夫特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)
    }

}

1
请为此同时发布快速版本。谢谢。
Salman Khakwani

@pinch:tnx。在iOS 12.1.3中仍然存在。
YvesLeBorg '19

20

如果不需要UITextView滚动,则不涉及子类的最简单的解决方案是简单地禁用文本视图的用户交互:

textField.userInteractionEnabled = NO;

2
如果那是在文本视图中,则可以省去点击链接等。应该注意的是,这不是想要隐藏选择/复制/粘贴,而是保持某种级别的交互的好方法。
barfoon

3
好吧,我本来以为那是显而易见的。
路加·雷德帕特

我将文本字段用作游戏中图片的标签,并且我不希望出现放大镜,也没有理由复制文本。这对我来说很棒。我在UIWebView中使用样式化的文本,并且同一行也可以正常工作。
JScarry

15

最简单的方法是创建覆盖canPerformAction:withSender的UITextView的子类:

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

这是能够键入文本但别无其他内容的最佳解决方案,并且允许对文本字段的点击不再“被拦截”。这可以满足OP的需求,甚至更多。
hlfcoding 2013年

13

当我在iOS 7的canPerformAction中返回NO时,会出现很多类似以下错误:

<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.

我的解决方案如下:

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

诀窍是在主队列的下一个循环中隐藏菜单控制器(显示之后)。


非常好。唯一的问题是我有2个textviews和一个textField,我想避免仅在textView字段上粘贴粘贴。如何识别谁叫canPerformAction?发送方变量是UIMenuController
Gaucho

1
它适用于UITextField(或UITextView)的子类。如果您不使用所创建的子类,它将不会有任何效果。例如,我创建了一个TextFieldWithoutCopyPaste,并在我不想拥有复制粘贴功能的地方使用了它。
亚当·沃纳

好吧,你是对的。但是在我的情况下,textView需要子类使用canPerformAction,而textField需要子类使用textFieldDidBeginEditing以便在显示键盘时为窗口添加动画。动画使用键盘移动窗口。我使用这种方法来避免键盘覆盖textField。
高乔2015年

10

这是禁用UITextView中的整个“选择/复制/粘贴”菜单的最简单方法

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{    
    [UIMenuController sharedMenuController].menuVisible = NO;
    return NO;    
}

也适用于UITextField。感谢分享。
Gaurav Srivastava

1
我仍在使用上述代码,但我正在获取菜单如何禁用该菜单,请帮助我。
Bittoo

4

从iOS 7开始,UITextView上有一个属性:

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

这使视图无法进行文本选择。对我来说很棒。


4

如果您希望将键盘替换UIPicker为,inputView(当然也将工具栏作为inputAccesotyView),那么此解决方法可能会有所帮助...

  • 实行 textFieldShouldBeginEditing:
  • 内放 textField.userInteractionEnabled = NO;
  • 然后,当您要关闭时UIPickerView,将其设置为YES。

这样,您就可以点击UITextField并显示从中进行选择的选项UIPickerView,这时您UITextField实际上不会对任何触摸事件做出反应(这包括触摸并按住以进行剪切,复制和粘贴)。但是,您必须记住在关闭您的设备时将其设置回YES,UIPickerView但是您将无法UIPickerView再次访问它。

失败的唯一时刻是用户通过点击并按住来开始UITextView,然后您将看到剪切的副本并再次粘贴。这就是为什么您应该始终验证输入的原因。这是我最容易想到的。另一个选择是使用UILabel只读文本,但是您会错过的许多强大功能UITextView


4

子类UITextView-Swift 4.0

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

多么奇怪 它仍然显示选项“粘贴”,但是如果按下则不要这样做...
iOS Flow

4

如果要禁用弹出窗口,请UITextField尝试使用此UITextFieldDelegate方法进行切换isUserInteractionEnabled

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    textField.isUserInteractionEnabled = false
    return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
    textField.isUserInteractionEnabled = true
    return true
}

3

这可以在情节提要(Xcode 6)中轻松完成。只需在Attributes Inspector中取消选中Editable和Selectable。您仍然可以滚动文本视图。在此处输入图片说明


3

这对我有用。确保您正在textView上调用resignFirstResponder

-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
  [self.textView resignFirstResponder];
  return NO;
}

2

在这里提供了一个有效的答案以禁用文本选择+放大镜,并保持启用的可点击链接,希望对您有所帮助:

经过相当长时间的尝试,我通过重写UITextView子类上的addGestureRecognizer来停止文本选择,放大并保持数据检测(链接可单击等),仅允许UILongPressGestureRecognizer延迟触摸结束:

UIUnselectableTextView.m

-(void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
    if([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] && gestureRecognizer.delaysTouchesEnded)
    {
        [super addGestureRecognizer:gestureRecognizer];
    }
}


2

您可以通过取消选中以下框来解决此问题:

在此处输入图片说明

或者,您可以像这样编程设置:

textView.selectable = false
textView.editable = false

1

我已经做了。在我上,UITextView我非常容易禁用剪切,复制,选择等选项。

我将放在放置了UIView的同一位置UITextView,但是在上self.view添加了以下touchDelegate方法:

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

    }
}

它为我工作。谢谢。


1

迅速

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

有关

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.

1

如果要向UITextView添加自定义选项但禁用现有功能,这是在Swift 3上执行的操作

要禁用复制,粘贴,剪切功能,请创建一个子类并覆盖以下内容:

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

在ViewController上,您的CustomTextView添加以下内容以添加选项:

  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)")

    }

1

此方法将完全禁用“选择”,“全选”,“粘贴”菜单。如果仍然采取其他措施,只需将其添加到if条件,如下所示。

- (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];
    }

0

您可以像这样创建类别:

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
这不是解决问题的好方法。首先,它影响一切,因为它属于类别。其次,将关联对象用于相当简单的任务可能会给以后带来更多的调试麻烦(当您忘记所做的事情时,为什么这样工作)而不是赚钱。我认为最好避免这种情况。使项目中的新程序员不太容易查找和调试代码,也无法理解它们。
Vive 2014年

0

子类化UITextView和覆盖- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer是禁用不需要的操作的另一种可能性。

使用gestureRecognizer-object 的类来决定是否应添加操作。


0

(SWIFT)如果只需要一个基本的文本字段,而没有菜单选项或放大镜,则创建一个UITextField的子类,将false返回给gestureRecognizerShouldBegin:

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

        return false
    }
}

这将绕过文本字段上的所有触摸功能,但仍允许您使用弹出键盘添加/删除字符。

如果使用情节提要,只需将新创建的类分配给文本字段,或者以编程方式创建文本字段:

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

basic.becomeFirstResponder()

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

迅捷3

为此,您需要将UITextView子类化并放入此方法。

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)
    }

0

UITextView具有两个可以满足您需要的属性:isSelectableisEditable

将isEditable设置为false可以避免用户编辑文本,而将isSelectable设置为false可以避免用户在textView中选择文本,因此这将阻止显示操作菜单。


0

请找到示例代码以供参考:

 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
    }

-1

用于func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { retrun bool }代替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
    }
}

创建一个名为StopPasteAction.swift的新类

import UIKit

class StopPasteAction: UITextField {

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

使用当前的TextField将类添加为新类

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.