如何从UITextField子类

时间:2017-05-14 09:35:41

标签: ios swift uitextfield uikit

背景

我已经创建了UITextField的子类,我想拦截用户输入的任何字符并执行一些验证。查看文档时,UITextField符合UIKeyInput,并且当用户在键盘上键入字符(documentation)时应调用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()无法正常工作,是否还有另一种方法来监控文字更改事件?

3 个答案:

答案 0 :(得分:1)

尝试使用此方法。将以下内容添加到CustomTextField类。它是EMAIL字段的处理程序 - 不允许两次输入“@”等等:

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
}


}

答案 1 :(得分:0)

对于你所要求的内容而言,这可能会有点过头了但是我知道在没有使用委托方法的情况下做这样的事情的最好方法是使用Reactive Functional Programming,这样就可以了使用Observable对象监听UITextField的事件。我有一些使用ReactiveKit更具体Bond的经验,因此您只需要一两行代码来实现您的需求。

答案 2 :(得分:0)

这是我最后为遇到同样问题的人所做的事情。

  • 我创建了一个私有变量,用于存储文本字段的最后一个有效状态。这样,如果对该字段的更新未通过验证,则可以拒绝更新&#39;通过还原。
  • 我的子类订阅了它自己的文本更改通知。这样,每次更改都可以触发验证(感谢建议Anton Novoselov

这里的代码包含一个简单的验证示例:

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
        }

    }

}