如何在 Swift 的 TextField 中禁用粘贴?

How to disable pasting in a TextField in Swift?

我有一个 TextField 和一个 numberPad,只有当它包含数字时函数才会运行。

如果用户在 TextField 中粘贴字母并单击确定,用户将导致应用程序崩溃。

如何在 TextField 中禁用粘贴?

您可以将 IBAction 附加到文本字段的已发送事件(编辑已更改),以在您键入时从文本中删除所有非数字,如下所示:

@IBAction func editingChanged(_ textField: UITextField) {
    textField.text?.removeAll { !("0"..."9" ~= [=10=]) }
}

这将允许用户粘贴到字段中,但它会从字符串中过滤掉所有非数字。

我同意 ,如果我是你,我会使用字符串检查并发出警告,这会让事情变得更容易。但是,如果禁用粘贴选项是您真正想要放入应用程序中的奇特功能,那么您需要做更多的工作。我将提供以下步骤。

第 1 步:您需要创建另一个 class 来扩展 UITextField。在这个例子中,我做了 CustomUITextField.

import Foundation
import UIKit  //Don't forget this

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

第 2 步:使用您的 ViewController 连接故事板。您需要像正常情况一样声明一个 IBOutlet

@IBOutlet var textFieldA: CustomUITextField?

@IBOutlet 旁边的圆圈连接到故事板中的 TextField。那么,这很重要也很容易被忽略:

  • 转到你的故事板
  • 点击目标TextField
  • Select Identity Inspector(第三个)
  • 将 class 更改为 CustomUITextField

下面提供了快速快照。

就是这样,希望这有效。

来源:

Main reference

如果您想了解更多关于 canPerformAction 方法的行为,虽然它是 Objective-C 版本,但概念是共享的 here

您可以为 UITextField 创建扩展并覆盖 canPerformAction:

override public func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
        return (action != "paste:") 
}

在实际的swift版本中(2.2转3.0)这个功能代码必须重构为:

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

    return true
}

对于Swift 3它改为:

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

    return true
}

对于Swift 5

最近添加了

UIResponder​Standard​Edit​Actions (iOS 10.0+),通过它我们可以安全地检查操作是否为 "paste"。

import UIKit

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

使用代码进行小修改,因为当您尝试使用任何功能(如剪切或其他功能)时,应用程序会崩溃。以下代码在 swift 3 上测试并且运行良好

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

详情

  • Xcode9.1,Swift4
  • Xcode 10.2 (10E125), 11.2 (11B52), Swift 5

解决方案 1

// class TextField: UITextField
extension UITextField {

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

解决方案 1 用法

let textField = UITextField(frame: CGRect(x: 50, y: 120, width: 200, height: 50))
textField.borderStyle = .roundedRect
view.addSubview(textField)

解决方案 2

import UIKit

// MARK: Enable/Disable textfield longpress actions

enum ResponderStandardEditActions {
    case cut, copy, paste, select, selectAll, delete
    case makeTextWritingDirectionLeftToRight, makeTextWritingDirectionRightToLeft
    case toggleBoldface, toggleItalics, toggleUnderline
    case increaseSize, decreaseSize

    var selector: Selector {
        switch self {
            case .cut:
                return #selector(UIResponderStandardEditActions.cut)
            case .copy:
                return #selector(UIResponderStandardEditActions.copy)
            case .paste:
                return #selector(UIResponderStandardEditActions.paste)
            case .select:
                return #selector(UIResponderStandardEditActions.select)
            case .selectAll:
                return #selector(UIResponderStandardEditActions.selectAll)
            case .delete:
                return #selector(UIResponderStandardEditActions.delete)
            case .makeTextWritingDirectionLeftToRight:
                return #selector(UIResponderStandardEditActions.makeTextWritingDirectionLeftToRight)
            case .makeTextWritingDirectionRightToLeft:
                return #selector(UIResponderStandardEditActions.makeTextWritingDirectionRightToLeft)
            case .toggleBoldface:
                return #selector(UIResponderStandardEditActions.toggleBoldface)
            case .toggleItalics:
                return #selector(UIResponderStandardEditActions.toggleItalics)
            case .toggleUnderline:
                return #selector(UIResponderStandardEditActions.toggleUnderline)
            case .increaseSize:
                return #selector(UIResponderStandardEditActions.increaseSize)
            case .decreaseSize:
                return #selector(UIResponderStandardEditActions.decreaseSize)
        }
    }
}

class TextField: UITextField {

    private var editActions: [ResponderStandardEditActions: Bool]?
    private var filterEditActions: [ResponderStandardEditActions: Bool]?

    func setEditActions(only actions: [ResponderStandardEditActions]) {
        if self.editActions == nil { self.editActions = [:] }
        filterEditActions = nil
        actions.forEach { self.editActions?[[=12=]] = true }
    }

    func addToCurrentEditActions(actions: [ResponderStandardEditActions]) {
        if self.filterEditActions == nil { self.filterEditActions = [:] }
        editActions = nil
        actions.forEach { self.filterEditActions?[[=12=]] = true }
    }

    private func filterEditActions(actions: [ResponderStandardEditActions], allowed: Bool) {
        if self.filterEditActions == nil { self.filterEditActions = [:] }
        editActions = nil
        actions.forEach { self.filterEditActions?[[=12=]] = allowed }
    }

    func filterEditActions(notAllowed: [ResponderStandardEditActions]) {
        filterEditActions(actions: notAllowed, allowed: false)
    }

    func resetEditActions() { editActions = nil }

    open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if let actions = editActions {
            for _action in actions where _action.key.selector == action { return _action.value }
            return false
        }

        if let actions = filterEditActions {
            for _action in actions where _action.key.selector == action { return _action.value }
        }

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

解决方案 2 用法

let textField = TextField(frame: CGRect(x: 50, y: 50, width: 200, height: 50))
textField.borderStyle = .roundedRect
view.addSubview(textField)
textField.setEditActions(only: [.copy, .cut, .paste])
//textField.filterEditActions(notAllowed: [.copy, .cut, .paste])
//textField.addToCurrentEditActions(actions: [.paste])

解决方案 2 的完整示例

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        addTextField(y: 50)
        addTextField(y: 100).setEditActions(only: [.copy, .cut, .paste])
        addTextField(y: 150).filterEditActions(notAllowed: [.copy, .cut, .paste])
    }

   @discardableResult func addTextField(y: CGFloat) -> TextField {
        let textField = TextField(frame: CGRect(x: 50, y: y, width: 200, height: 34))
        textField.borderStyle = .roundedRect
        textField.text = "Text"
        view.addSubview(textField)
        return textField
    }
}
class CustomUITextField: UITextField {
    override public func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(cut(_:)) ||
           action == #selector(copy(_:)) ||  
           action == #selector(UIResponderStandardEditActions.paste(_:)) || 
           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
    }
}

我已经为 textField 创建了自定义 class。当您想对文本字段执行 enable/disable 操作时,我已经处理过这种情况。您可以根据需要自定义代码。为文本字段上的 enable/disable 操作设置 isActionsEnabled true/false。

更喜欢使用

return super.canPerformAction(action, withSender: sender)

而不是

return true

因为在某些情况下返回 true 可能会导致崩溃。

这是我的代码,

open class MyTextFieldEffect : UITextField {

    var isActionsEnabled = true {
        didSet {
            reloadInputViews()
        }
    }

override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        /* disable particular actions
        if (action == #selector(paste(_:)) || action == #selector(copy(_:)) || action == #selector(select(_:)) || action == #selector(cut(_:)) || action == #selector(delete(_:)) || action == #selector(replace(_:withText:))  || action == #selector(select(_:))  || action == #selector(selectAll(_:)) || action == #selector(insertText(_:)) || action == #selector(draw(_:))) && !isActionsEnabled {
            return false
        }
        return super.canPerformAction(action, withSender: sender)
                                           */

       //disable all actions
        if !isActionsEnabled {
            return false
        }

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

Swift 4.1 此代码在 ViewController.

下运行良好

1) 禁用所有选项(复制、粘贴、删除......等)

extension UITextField {

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

2) 启用特定选项(select、select全部...等)

extension UITextField {

open override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
    return action == #selector(UIResponderStandardEditActions.select(_:)) || action == #selector(UIResponderStandardEditActions.selectAll(_:))
}

如果您想在 TEXTFIELD 上打开日期选择器或选择器视图,请单击下面的代码。

在您的 class 中添加以下两种方法。

//Hide Menu View
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {

    if YOURTEXTFIELD.isFirstResponder {
        DispatchQueue.main.async(execute: {
            (sender as? UIMenuController)?.setMenuVisible(false, animated: false)
        })
        return false
    }

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

//必须实施

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
            return false
}

Swift 5

如果您想阻止应用中每个文本字段的粘贴操作

extension UITextField {
    override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        return action == #selector(UIResponderStandardEditActions.paste(_:)) ?
            false : super.canPerformAction(action, withSender: sender)
    }
}

编辑

如果要阻止将选择器作为输入视图的文本字段的粘贴操作,请添加 guard,如下所示:

extension UITextField {
    override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        guard inputView != nil else { return super.canPerformAction(action, withSender: sender) }

        return action == #selector(UIResponderStandardEditActions.paste(_:)) ?
            false : super.canPerformAction(action, withSender: sender)
    }
}

警告:第二种解决方案将允许用户粘贴数字字段。

我确实用过这段代码。这是有效的。

override func canPerformAction(_ action: Selector, withSender sender: Any?)  Bool {

    if YOURTEXTFIELD.isFirstResponder {
        DispatchQueue.main.async(execute: {
            (sender as? UIMenuController)?.setMenuVisible(false, animated: false)
        })
        return false
    }

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