Swift 中的 UITextField – 输入

问题描述

我现在正在 Swift 中编写自定义 UITextField,遇到以下情况:

class MyField: UITextField {
    
    open override var text: String? {
        didSet {
            // some logic that normalizes the text
        }
    }

    private func setup() { //called on init
        addTarget(self,action: #selector(textEditingChanged(_:)),for: .editingChanged)
    }

    @objc func textEditingChanged(_ sender: UITextField) {
    }
}

现在,在对此进行测试时,我观察到,当用户键入内容时,会调用 textEditingChanged,但不会调用 text.didSet。就此而言,text.willSet 也不是。但是,在 textEditingChanged 中,文本字段的文本已更新为新值。

谁能解释一下这里发生了什么? Swift 是故意绕过 setter 的吗?我应该如何知道何时/是否调用 setter,UIKit 中是否有任何逻辑?

解决方法

UITextField 的 text 属性仅供外部使用,当您键入 UIKit 时会在内部处理更新。无法真正说出幕后发生了什么,因为我们无法访问 UIKit 实现,但问题的一个可能答案是 UITextField 正在使用另一个内部变量来存储文本属性.当您获取 text 属性时,您实际上是在获取该内部值,而在设置它时,您也在设置该内部值。当然在引擎盖下它有点复杂,但它可能看起来像这样(SampleUITextField 代表UITextField):

// This how the UITextField implementation may look like
class SampleUITextField {
    private var internalText: String = ""

    var text: String  {
        get {
            internalText
        } set {
            internalText = newValue
        }
    }

    // This method is just to demonstrate that when you update the internalText didSet is not called 
    func updateInternalTextWith(text: String) {
       internalText = text
    }
}

当你子类化它时,它看起来像:

class YourTextField: SampleUITextField {
    override var text: String {
        didSet {
            print("DidSet called")
        }
    }
}

现在,当您直接设置文本属性时,会调用 didSet,因为 text 值会更新。你可以检查一下:

let someClass = YourTextField()
someClass.text = "Some new text"
// didSet is called 

但是现在当您调用 updateInternalTextWith 时,不会调用 didSet

let someClass = YourTextField()
someClass.updateInternalTextWith(text: "new text")
// didSet is not called 

那是因为您没有直接更新文本值,而只是更新内部文本属性。键入时会调用类似的方法来更新 UITextField 的内部文本变量,这就是在您的情况下未调用 didSet 的原因。

因此,当我们希望在文本属性更改时收到通知时,仅覆盖 text 变量是不够的,我们需要使用 delegate(UITextFieldDelegate) 方法或通知 ({ {1}}) 以捕捉实际更改。

,

您想在 UITextField 上执行哪种格式设置?

这不应该由 ViewModel(或管理包含此文本字段的视图的业务逻辑的任何模型)来代替吗?

此外,您不应该使用 didSet 来执行更改,它主要用于允许您响应更改,而不是链接另一个更改。