Swift 中的 UITextField – 键入时不调用文本 didSet
UITextField in Swift – text didSet not called when typing
我现在正在 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 吗?我怎么知道 when/if setter 被调用了,在 UIKit 中有什么逻辑吗?
您要在 UITextField 上执行哪种格式设置?
难道不应该由 ViewModel(或管理包含此文本字段的视图的业务逻辑的任何模型)负责吗?
此外,您不应该使用 didSet
来执行更改,它主要用于允许您响应更改,而不是链接另一个更改。
UITextField
的文本属性 仅供外部使用,当您输入时UIKit
会在内部处理更新。无法真正知道幕后发生了什么,因为我们无法访问 UIKit
实现,但问题的一个可能答案是 UITextField
正在使用另一个内部变量来存储文本 属性。当您获取文本 属性 时,您实际上是在获取该内部值,而当您设置它时,您也在设置该内部值。当然在引擎盖下它有点复杂,但它可能看起来像这样(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
) 方法或通知(textFieldDidChange
) 捕捉实际变化。
我现在正在 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 吗?我怎么知道 when/if setter 被调用了,在 UIKit 中有什么逻辑吗?
您要在 UITextField 上执行哪种格式设置?
难道不应该由 ViewModel(或管理包含此文本字段的视图的业务逻辑的任何模型)负责吗?
此外,您不应该使用 didSet
来执行更改,它主要用于允许您响应更改,而不是链接另一个更改。
UITextField
的文本属性 仅供外部使用,当您输入时UIKit
会在内部处理更新。无法真正知道幕后发生了什么,因为我们无法访问 UIKit
实现,但问题的一个可能答案是 UITextField
正在使用另一个内部变量来存储文本 属性。当您获取文本 属性 时,您实际上是在获取该内部值,而当您设置它时,您也在设置该内部值。当然在引擎盖下它有点复杂,但它可能看起来像这样(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
) 方法或通知(textFieldDidChange
) 捕捉实际变化。