【问题标题】:How disable Copy, Cut, Select, Select All in UITextView如何在 UITextView 中禁用复制、剪切、选择、全选
【发布时间】:2010-11-28 10:38:56
【问题描述】:

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

【问题讨论】:

  • 放置 [UIMenuController sharedMenuController].menuVisible = NO; in - (BOOL) canPerformAction:(SEL)action withSender:(id)sender 方法。

标签: ios objective-c uitextview


【解决方案1】:

禁用粘贴板操作的最简单方法是创建UITextView 的子类,该子类覆盖canPerformAction:withSender: 方法以针对您不想允许的操作返回NO

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

另见UIResponder

【讨论】:

  • @rpetrichm,我使用了你的解决方案,复制/粘贴/剪切/选择/选择所有选项已禁用,但仍有替换...|BIU|定义选项。我想禁用那个完整的菜单。
  • - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; } - 阻止所有选项
  • 很好,但是我不知道如何为 UISearchBar 执行此操作 - 因为里面还有一个文本字段,我想我可以覆盖 UISearchBar 子类的方法,但不幸的是这不起作用.有什么想法吗?
  • 我有很多控件。如何只允许在一个控件上复制粘贴?
  • 这不适用于 UITextView,尽管我已经在 SO 的许多地方看到了这个解决方案。有没有人想出如何做到这一点?
【解决方案2】:

我已经做到了。在我的UITextView 上,我很容易禁用剪切、复制、选择等选项。

我在放置UITextView 的同一位置放置了UIView,但在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;
        }

    }
}

它对我有用。谢谢。

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 也适用于 UITextField。感谢分享。
    • 我正在使用上面的代码,但我正在获取菜单如何禁用它,请帮助我。
    【解决方案4】:

    最简单的方法是创建一个覆盖 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 的要求,甚至更多。
    【解决方案5】:

    继承 UITextView 并覆盖 canBecomeFirstResponder:

    - (BOOL)canBecomeFirstResponder {
        return NO;
    }
    

    注意,这只适用于不可编辑的 UITextViews!还没有在可编辑的上测试过...

    【讨论】:

    • 我认为return NO; 上的- (BOOL)canPerformAction:(SEL)action withSender:(id)sender 方法是一个更好的选择。
    【解决方案6】:

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

    textField.userInteractionEnabled = NO;
    

    【讨论】:

    • 如果那是文本视图中的内容,这将消除点击链接等。应该注意的是,这不是一个想要隐藏选择/复制/粘贴的好解决方案,但也要保持启用某种程度的交互。
    • 好吧,我原以为这很明显。
    • 我在游戏中使用文本字段作为图片的标签,我不希望出现放大镜,也没有理由复制文本。这对我很有用。我在 UIWebView 中使用样式文本,并且同一行也可以在那里工作。
    【解决方案7】:

    如果您想在您的应用程序的 all UITextView 上禁用剪切/复制/粘贴,您可以使用 category

    @implementation UITextView (DisableCopyPaste)
    
    - (BOOL)canBecomeFirstResponder
    {
        return NO;
    }
    
    @end
    

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

    【讨论】:

    • 你也可以只把它放在你需要这种行为的 /, 文件中。
    • 这仅作为一种副作用起作用,并防止UITextView 在例如可编辑和触摸时按预期运行。覆盖canPerformAction:withSender: 很多;这就是协议的用途。
    • 您不能安全地使用类别覆盖方法。这是未定义的行为。您必须子类化才能安全地覆盖方法。
    • 注意:这将适用于您应用程序中的所有 UITextView。大多数时候都不理想。
    • 还要注意Appleadvises against this:“如果某个类别中声明的方法名称与原始类中的方法相同,或者同一类上的另一个类别中的方法(或甚至是超类),对于在运行时使用哪种方法实现的行为未定义...... [并且] 在使用类别将方法添加到标准 Cocoa 或 Cocoa Touch 类时可能会导致问题。”
    【解决方案8】:

    这对我来说是最好的解决方案:

    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

    【讨论】:

    • 不错,这里没有子类化!
    • 我喜欢这里的胶带和打包线方法。我希望我能再次为你 +1 :) 相反,这里是一颗金星:i.imgur.com/EXLFt1Z.jpg
    【解决方案9】:

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

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

    【讨论】:

      【解决方案10】:

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

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

      这可以防止视图允许文本选择。非常适合我。

      【讨论】:

        【解决方案11】:

        当我在 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 个文本视图和一个文本字段,我想避免仅在文本视图字段上复制粘贴。如何标识谁调用了canPerformAction?发件人变量是 UIMenuController
        • 它适用于 UITextField(或 UITextView)的子类。如果你不使用你创建的子类,它不会有任何效果。例如。我创建了一个 TextFieldWithoutCopyPaste 并在我不想拥有复制粘贴功能的地方使用它。
        • 好的,你是对的。但在我的情况下,textView 需要子类才能使用 canPerformAction,而 textField 需要子类才能使用 textFieldDidBeginEditing 以便在显示键盘时为窗口设置动画。动画用键盘移动窗口。我使用这种方法来避免键盘覆盖 textField。
        【解决方案12】:

        您可以像这样创建类别:

        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
        

        【讨论】:

        • 这不是解决问题的好方法。首先,它会影响一切,因为它在类别中。其次,将关联对象用于相当简单的任务可能会在以后的调试中遇到更多麻烦(当您忘记所做的事情时为什么会这样工作)而不是获利。在我看来,尽可能避免它是件好事。使项目中的新程序员更不容易找到、调试和理解代码。
        【解决方案13】:

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

        • 实施textFieldShouldBeginEditing:
        • 内放textField.userInteractionEnabled = NO;
        • 那么当你即将关闭UIPickerView时,将其设置为YES。

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

        唯一失败的时刻是当用户开始点击并按住UITextView 时,您会第一次看到剪切复制和粘贴。这就是为什么您应该始终验证您的输入。这是我能想到的最简单的。另一种选择是将UILabel 用于只读文本,但您会错过UITextView 的许多出色功能。

        【讨论】:

          【解决方案14】:

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

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

          【讨论】:

            【解决方案15】:

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

            【讨论】:

            • 请为此发布一个快速版本。谢谢你。
            • @pinch:tnx。在 iOS 12.1.3 中仍然存在。
            【解决方案16】:

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

            经过很长时间的尝试,我设法通过覆盖 UITextView 子类上的 addGestureRecognizer 来停止文本选择、放大和保持数据检测(链接可点击等),只允许 UILongPressGestureRecognizer 延迟触摸结束:

            UIUnselectableTextView.m

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

            【讨论】:

              【解决方案17】:

              这可以在情节提要 (Xcode 6) 中轻松完成。只需取消选中 Attributes Inspector 中的 Editable 和 Selectable。你仍然可以滚动文本视图。

              【讨论】:

                【解决方案18】:

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

                【讨论】:

                  【解决方案19】:

                  斯威夫特

                  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.
                  

                  【讨论】:

                    【解决方案20】:
                    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)}
                    

                    【讨论】:

                      【解决方案21】:

                      您可以在情节提要中通过取消选中这些框来解决此问题:

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

                      textView.selectable = false
                      textView.editable = false
                      

                      【讨论】:

                        【解决方案22】:

                        对于 Swift 3,它改为:

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

                        【讨论】:

                          【解决方案23】:

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

                          【讨论】:

                            【解决方案24】:

                            使用func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -&gt; 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 添加类新类

                            【讨论】:

                              【解决方案25】:

                              如果您想为 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)")
                              
                                  }
                              

                              【讨论】:

                                【解决方案26】:

                                此方法将完全禁用选择、全选、粘贴菜单。如果您仍然执行其他操作,则只需将其添加到 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];
                                    }
                                

                                【讨论】:

                                  【解决方案27】:

                                  子类 UITextView - swift 4.0

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

                                  【讨论】:

                                  • 有多奇怪?它仍然显示“粘贴”选项,但如果按下则不要这样做...
                                  【解决方案28】:

                                  如果您想禁用UITextField 的弹出窗口,请尝试使用此UITextFieldDelegate 方法来切换isUserInteractionEnabled

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

                                  【讨论】:

                                  • 我不喜欢对这样的小任务进行子类化和覆盖函数,因为它只会造成更多的混乱,所以这种委托方法对我很有效。
                                  • 这对我来说真的很棒,正是我想要的!
                                  【解决方案29】:

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

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

                                  【讨论】:

                                    【解决方案30】:

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

                                     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
                                        }
                                    

                                    【讨论】:

                                      猜你喜欢
                                      • 1970-01-01
                                      • 2014-03-21
                                      • 2014-09-07
                                      • 2018-01-19
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 2013-06-27
                                      • 1970-01-01
                                      相关资源
                                      最近更新 更多