如何在程序中编程禁用UITextField的复制粘贴选项

54

我正在制作一个注册提示框,其中有一个UITextField,用户可以在其中输入他们的注册号码。几乎所有东西都已经完成了,但是我想通过编程方式从文本字段中删除复制粘贴功能,因为没有InterfaceBuilder版本的文本字段,所以我不知道如何做到这一点。

以下是我的UIAlertview...

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

}

3
我认为这不是重复的内容——这来自于UITextField,而其他的是针对textView的。因此,一些textView的解决方案并不适用。 - Jeff
对于 iOS 7 https://dev59.com/oWUo5IYBdhLWcg3wzSIq#15746164 - iPatel
在编程中,你会遇到相同的问题,但有不同的解决方案。 - Pablo Blanco
17个回答

55

这篇文章有很多不错的解决方案:如何在UITextView中禁用复制、剪切、选择、全选

我最喜欢的方法是重写canPerformAction:withSender:

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

1
在iOS 7上,我遇到了很多错误。以下是一些错误文本:CGContextSetFillColorWithColor:无效的上下文0x0。这是一个严重的错误。此应用程序或其使用的库正在使用无效的上下文,从而导致系统稳定性和可靠性的总体降级。此通知是礼貌的:请解决此问题。它将在即将推出的更新中成为致命错误。 - Matt Becker
1
针对iOS 7,如何禁用UITextField的粘贴功能? - iPatel
请注意,还有一个“替换”菜单项,无法通过canPerformAction:withSender安全地禁用。要关闭粘贴,请通过UITextFieldUIInputTraits协议禁用拼写检查。 - Adam Kaplan

33

如果你可以接受子类化,Storyboard的用户可能会对这个解决方案感兴趣。

我认为通过扩展或协议很难实现这一点。

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链接


3
我认为默认情况应该是“return super.canPerformAction(action, withSender: sender)”而不是“return true”。否则,菜单将显示可能与UITextField无关的所有操作。 - Hoang HUA
@HoangHUA 很棒的发现。 - gujci

23

我已经找到了一种方法,使用扩展和关联对象而不需要子类化。我使用只读属性来禁用粘贴/剪切,但是这个示例可以进行调整。

Swift 3 已更新至 2016 年 11 月 27 日。

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

}

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

}

这绝对是最灵活的方法。 - Darcy Rayner

23

适用于iOS8.0+,Xcode 6.0.1,启用ARC

希望能省去像我这样的初学者实现此功能所需的时间...

要实现禁用复制/粘贴/剪切等功能,您必须子类化UITextField并覆盖...

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender

实现此操作...

创建一个新类,它是UITextField的子类(即在您的应用文件夹中包含一个新的.h和.m文件)。所以File->New->"Cocoa Touch Class"->下一步->"PasteOnlyUITextField"(例如),子类为"UITextField"->下一步->创建。

一旦我们为名为"PasteOnlyUITextField"的新的UITextField子类创建了.h和.m文件...

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

现在确保在需要使用它的地方导入PasteOnlyUITextField.h,例如您的YourUIViewController.h文件...

#import "PasteOnlyUITextField.h"

现在必须使用子类,可以通过编程或使用身份检查器来实现

PasteOnlyUITextField *pasteOnlyUITextField = [[PasteOnlyUITextField alloc] init...];

或者...

选择UITextField并转到身份检查器,选择其类。

identity inspector

您可以根据需要更改与菜单选项相关联的逻辑...

希望这可以帮助!感谢所有原始贡献者。


1
不要在方法结尾处返回 blanket false。你是否记录了所有接收到的操作?其中相当多的操作,甚至大部分都是私有的苹果操作,不是你需要控制的。例如,_accessibilitySpeak 可能是为视障用户设计的功能,你可能需要它。_transliterateChinese 是另一个有趣的操作。最好禁用你绝对不需要的操作,并将其他操作委托给超类。 - Adam Kaplan
我想要的是一个只有一个操作——粘贴的文本框。我有一个非常基本的应用程序,名为“C/F Convert”和“Meter Fairy”,使用这种策略,两者都已经通过了苹果的审核。我的答案提供了我认为更详细的描述,以便通过子类化UITextField来实现这一目标,并且您可以自由选择最适合您的应用程序的方法。如果苹果想要保护这个特定的类,那么他们将阻止您直接对其进行子类化,而您必须创建一个类别来更改允许的属性或方法。 - serge-k
@AdamKaplan 因为只有在PASTE情况下才返回TRUE,所以我认为答案没有问题。 - Abdurrahman Mubeen Ali
但问题是在问相反的情况。对于这种情况,@PengOne的答案是正确的。 - Abdurrahman Mubeen Ali
1
我了解答案实现了问题的目标。但是,这样做会导致无关的副作用,影响可访问性,是一种不良实践。如果其他人复制此答案,并导致他们的应用程序对盲人/聋人的用户使用效果不佳,那将非常糟糕。PengOne的答案是正确的,而且我几年前就点赞了它! - Adam Kaplan

13

在Swift中,如果你想让你的文本框禁用所有UIResponderStandardEditActions(剪切、复制、粘贴、查找、共享、选择),请在UITextFieldDelegate中使用以下代码。

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

func textFieldDidEndEditing(_ textField: UITextField) {
    textField.isUserInteractionEnabled = true
}

这在iOS 14,Xcode 12上对我有帮助。谢谢。canPerformAction版本对我不起作用。 - Mr. G
还适用于iOS 16.x和Xcode 14.3。 - xhinoda

12

在ViewController.m中实现此方法,该方法将帮助您禁用UITextField上的选项。

它包括对应的UITextField上的粘贴、选择、全选和复制选项。

当您想要将其用于密码或DateOfBirth或其他任何目的时,此方法在UITextField的情况下非常有用。

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

9
在iOS 9中,我们可以隐藏键盘上的复制粘贴栏。
-(void) customMethod{

   yourTextField.inputAssistantItem.leadingBarButtonGroups = @[];
   yourTextField.inputAssistantItem.trailingBarButtonGroups = @[];

}

哈!这很酷。我要提醒你,你可能也会删除一些盲人用户、亚洲语言用户和“定义”选项。 - Adam Kaplan

7

Swift 5解决方案:

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

    return true
}

3

在您的视图控制器中尝试此方法

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

3

这是针对iOS 10及更早版本(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)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接