Ios 如何在UITextField子类中从键盘截取文本输入事件 背景

Ios 如何在UITextField子类中从键盘截取文本输入事件 背景,ios,swift,uitextfield,uikit,Ios,Swift,Uitextfield,Uikit,我已经创建了UITextField的一个子类,我想截取用户输入的任何字符并执行一些验证。查看文档,UITextField符合uikeyinport,当用户在键盘上键入字符时,应调用insertText()方法() 下面是一个非常基本的示例: import UIKit class CustomTextField: UITextField { override func insertText(_ text: String) { print("Character Typed

我已经创建了UITextField的一个子类,我想截取用户输入的任何字符并执行一些验证。查看文档,UITextField符合
uikeyinport
,当用户在键盘上键入字符时,应调用
insertText()
方法()

下面是一个非常基本的示例:

import UIKit

class CustomTextField: UITextField {

    override func insertText(_ text: String) {
        print("Character Typed: \(text)")  // never executes
        super.insertText(text)
    }

    override func deleteBackward() {
        print("deleting character") // executes
        super.deleteBackward()
    }

}
根据注释,从不调用
insertText
。相反,
deleteBackward()
(也来自UIKeyInput)按预期调用

为什么不使用UITextFieldDelegate? 我创建子类的原因是控件将在整个应用程序中重复使用。如果有办法将验证逻辑封装到控件中,那么让每个具有字段实例的ViewController重新实现验证逻辑是没有意义的

虽然我可以通过让我的子类符合
UITextFieldDelegate
,然后设置
delegate=self
,来绕过这个问题,但我会失去任何其他对象作为字段委托的能力,从而产生一个新问题

问题: 在UITextField的子类中截取键盘字符的最佳方法是什么


覆盖
insertText()
似乎不起作用,因此是否有其他方法来监视文本更改事件?

尝试使用这种方法。将以下内容添加到CustomTextField类中。例如电子邮件字段-不允许输入两次“@”,等等:

class CustomTextField: UITextField {

override func awakeFromNib() {
    self.addTarget(self, action: #selector(self.textDidchange), for: .editingChanged)
    self.delegate = self
}

    func textDidchange() {
//        print(self.text)
    }



}

extension CustomTextField: UITextFieldDelegate {


func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    return handleEmailField(withRange: range, withReplacementString: string)

}

func handleEmailField(withRange range: NSRange, withReplacementString replacementString: String) -> Bool {
    var illegalCharactersSet = CharacterSet.init(charactersIn: "?><,\\/|`~\'\"[]{}±#$%^&*()=+")

    let currentString = self.text! as NSString

    let newString = currentString.replacingCharacters(in: range, with: replacementString)

    if currentString.length == 0 && replacementString == "@" {
        return false
    }

    if currentString.contains("@") {
        illegalCharactersSet = CharacterSet.init(charactersIn: "?><,\\/|`~\'\"[]{}±#$%^&*()=+@")
    }

    let components = replacementString.components(separatedBy: illegalCharactersSet)
    if components.count > 1 {
        return false
    }

    return newString.characters.count <= 40
}


}
class CustomTextField:UITextField{
重写func awakeFromNib(){
self.addTarget(self,action:#选择器(self.textDidchange),for:.editingchange)
self.delegate=self
}
func textDidchange(){
//打印(self.text)
}
}
扩展CustomTextField:UITextFieldDelegate{
func textField(textField:UITextField,shouldChangeCharacters范围:NSRange,replacementString:string)->Bool{
return handleEmailField(withRange:range,withReplacementString:string)
}
func handleEmailField(范围:NSRange,替换字符串替换字符串:字符串)->Bool{
var illegalCharacterSet=CharacterSet.init(charactersIn:?>1{
返回错误
}

return newString.characters.count尝试使用此方法。将以下内容添加到CustomTextField类中。例如,它是电子邮件字段的处理程序-不允许输入两次“@”,等等:

class CustomTextField: UITextField {

override func awakeFromNib() {
    self.addTarget(self, action: #selector(self.textDidchange), for: .editingChanged)
    self.delegate = self
}

    func textDidchange() {
//        print(self.text)
    }



}

extension CustomTextField: UITextFieldDelegate {


func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    return handleEmailField(withRange: range, withReplacementString: string)

}

func handleEmailField(withRange range: NSRange, withReplacementString replacementString: String) -> Bool {
    var illegalCharactersSet = CharacterSet.init(charactersIn: "?><,\\/|`~\'\"[]{}±#$%^&*()=+")

    let currentString = self.text! as NSString

    let newString = currentString.replacingCharacters(in: range, with: replacementString)

    if currentString.length == 0 && replacementString == "@" {
        return false
    }

    if currentString.contains("@") {
        illegalCharactersSet = CharacterSet.init(charactersIn: "?><,\\/|`~\'\"[]{}±#$%^&*()=+@")
    }

    let components = replacementString.components(separatedBy: illegalCharactersSet)
    if components.count > 1 {
        return false
    }

    return newString.characters.count <= 40
}


}
class CustomTextField:UITextField{
重写func awakeFromNib(){
self.addTarget(self,action:#选择器(self.textDidchange),for:.editingchange)
self.delegate=self
}
func textDidchange(){
//打印(self.text)
}
}
扩展CustomTextField:UITextFieldDelegate{
func textField(textField:UITextField,shouldChangeCharacters范围:NSRange,replacementString:string)->Bool{
return handleEmailField(withRange:range,withReplacementString:string)
}
func handleEmailField(范围:NSRange,替换字符串替换字符串:字符串)->Bool{
var illegalCharacterSet=CharacterSet.init(charactersIn:?>1{
返回错误
}

return newString.characters.count对于您的请求,这可能是一种过分的做法,但我知道,在不使用委托方法的情况下执行此操作的最佳方法是使用反应式函数编程,这样就可以使用可观察对象侦听UITextField的事件从逻辑上来说,你只需要一两行代码就可以实现你所需要的。

也许这对你的要求来说是一种过分的杀伤力,但我所知道的不使用委托方法做这件事的最好方法是使用反应式函数编程,这样就可以用一个可观察的对象。我有一些使用更具体的东西的经验,你只需要一两行代码就可以实现你所需要的东西。

以下是我为任何有同样问题的人所做的

  • 我创建了一个私有变量,用于存储文本字段的最后有效状态。这样,如果对字段的更新未通过验证,则可以通过还原来“拒绝”更新
  • 我让我的子类订阅它自己的文本更改通知。这样,每次更改都可以触发验证(感谢您的建议)
下面是带有一个简单验证示例的代码:

import UIKit

class CustomTextField: UITextField {

    // Keep a copy of the last valid state so we can revert a change if it fails validation
    private var lastValidText: String?

    // Subscribe to 'editing changed' notofications from self
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addTarget(self, action: #selector(textDidChange), for: .editingChanged)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addTarget(self, action: #selector(textDidChange), for: .editingChanged)
    }

    func textDidChange() {

        let validationRegex = "^(a|e|i|o|u)+$"

        if let currentText = self.text, currentText != "" {

            if currentText.range(of: validationRegex, options: .regularExpression) != nil {

                // The update is valid - update the last valid state
                lastValidText = currentText

            } else {

                // The udate failed validation - revert
                self.text = lastValidText
            }

        } else {

            // The field is empty. This is a valid state so reset last valid state to nil
            self.text = nil
            lastValidText = nil
        }

    }

}

以下是我为所有有同样问题的人所做的事情

  • 我创建了一个私有变量,用于存储文本字段的最后有效状态。这样,如果对字段的更新未通过验证,则可以通过还原来“拒绝”更新
  • 我让我的子类订阅它自己的文本更改通知。这样,每次更改都可以触发验证(感谢您的建议)
下面是带有一个简单验证示例的代码:

import UIKit

class CustomTextField: UITextField {

    // Keep a copy of the last valid state so we can revert a change if it fails validation
    private var lastValidText: String?

    // Subscribe to 'editing changed' notofications from self
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addTarget(self, action: #selector(textDidChange), for: .editingChanged)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addTarget(self, action: #selector(textDidChange), for: .editingChanged)
    }

    func textDidChange() {

        let validationRegex = "^(a|e|i|o|u)+$"

        if let currentText = self.text, currentText != "" {

            if currentText.range(of: validationRegex, options: .regularExpression) != nil {

                // The update is valid - update the last valid state
                lastValidText = currentText

            } else {

                // The udate failed validation - revert
                self.text = lastValidText
            }

        } else {

            // The field is empty. This is a valid state so reset last valid state to nil
            self.text = nil
            lastValidText = nil
        }

    }

}

感谢您的建议。根据我对使用UITextFieldDelegate的评论,您必须在使用此方法的每个ViewController中重新实现使用该字段的逻辑。如果可能,我希望在子类中处理文本更改事件。谢谢,这确实允许我检测事件。现在的问题是与
insertText()不同
,我无法访问更改前的文本。如果更改未通过验证,我希望文本保持尝试更改前的状态。无论是否插入或删除字符,都会触发此操作。我可能会尝试添加变量以存储上次有效状态。如果验证失败,我将还原到最后一个有效状态,如果它通过了,我将更新最后一个有效状态。干杯。这肯定会起作用,但现在文本字段的委托不能是其他任何内容。请注意,如果您不使用委托来监视字段,这可能不是问题