Swift 中 UI 元素的通用 `getText()` 函数
Generic `getText()` function for UI elements in Swift
很多动作需要检查label.text
、textView.text
、textField.text
等内容是否为nil
所以我创建了一些扩展:
extension UILabel {
func getText() -> String {
return self.text ?? ""
}
}
extension UITextField {
func getText() -> String {
return self.text ?? ""
}
}
extension UITextView {
func getText() -> String {
return self.text ?? ""
}
}
这些扩展非常多余。类似的情况是当我需要将 Int
、Double
、Float
等转换为另一种数字格式时。我想要的是一个简单的 toInt()
,当出现问题时,return 为 -1
或 0
。
那么如何为 toString()
或 toInt()
创建通用函数?我在 Apple 文档中阅读了有关 extensions and generics 的信息,但我没有找到解决问题的方法。
最后,我尝试用相同的扩展扩展UIView
,因为它是UILabel
等的超类,但我不能调用getText()
。
那么创建通用扩展的好方法是什么?
对于第一个问题,您应该只扩展 UIView
并检查它是否是标签、文本字段或带有 if let
的文本视图。
extension UIView {
func getText() -> String {
if let label = self as? UILabel {
return label.text ?? ""
} else if let textField = self as? UITextField {
return textField.text ?? ""
} else if let textView = self as? UITextView {
return textView.text ?? ""
}
return ""
}
}
与创建协议相比,此方法的优势在于您可以轻松扩展它,即使 text/title 可以使用其他方法检索,例如 UIButton
.
[...]
else if let button = self as? UIButton {
return button.title(for: .normal) ?? ""
}
创建一个带有扩展的协议,然后扩展每个 class 以采用该协议:)
protocol GetTextProtocol {
var text: String? {get}
func getText() -> String
}
extension GetTextProtocol {
func getText() -> String {
return self.text ?? ""
}
}
extension UILabel: GetTextProtocol {
var text: String? {
return self.text
}
}
extension UITextView: GetTextProtocol {
var text: String? {
return self.text
}
}
这里有一些可疑的事情:当出现问题时,我不希望任何 toInt()
函数只是 return -1 或 0。 Swift 有一个很好的可选系统是有原因的,returning -1 或 0 引入了几个明显的陷阱。我也不知道你打算做什么来实现 getText()
在 UIView
上。许多视图没有文本。我不知道这个实现意味着什么或做什么。
所以我将忽略这两个细节并专注于主要问题,这似乎是您的扩展的冗余。有一种更简洁的方法,即通过协议扩展来减少重复。
protocol TextProviding {
var text: String? { get }
}
extension TextProviding {
// Following your example here. I would prefer calling
// this a `var textOrEmpty: String` but same idea.
func getText() -> String {
return text ?? ""
}
}
// To conform additional types, you just need 1 line
extension UILabel: TextProviding { }
extension UITextField: TextProviding { }
编辑:正如一些人指出的那样,将上面的代码与 extension UITextView: TextProviding { }
一起使用是行不通的,因为 UITextView
的 text
是 String!
,不是 String?
。不幸的是,这意味着如果您希望它也适用于 UITextView,您应该将 var text
要求重命名为其他名称(这意味着您将需要几行额外的代码来手动符合 UILabel
和 UITextField
).
protocol TextProviding {
var string: String? { get }
}
extension TextProviding {
var stringOrEmpty: String {
return string ?? ""
}
}
extension UILabel: TextProviding {
var string: String? { return text }
}
extension UITextField: TextProviding {
var string: String? { return text }
}
extension UITextView: TextProviding {
var string: String? { return text }
}
与此处发布的所有其他答案相比,one-liner 减少了冗余代码 (您想要的)。无需疯狂 condition-checks 或多个扩展。
extension UIView {
func getText() -> String? {
return self.responds(to: #selector(getter: UILabel.text)) ?
self.perform(#selector(getter: UILabel.text))?.takeUnretainedValue() as? String : nil
}
}
很多动作需要检查label.text
、textView.text
、textField.text
等内容是否为nil
所以我创建了一些扩展:
extension UILabel {
func getText() -> String {
return self.text ?? ""
}
}
extension UITextField {
func getText() -> String {
return self.text ?? ""
}
}
extension UITextView {
func getText() -> String {
return self.text ?? ""
}
}
这些扩展非常多余。类似的情况是当我需要将 Int
、Double
、Float
等转换为另一种数字格式时。我想要的是一个简单的 toInt()
,当出现问题时,return 为 -1
或 0
。
那么如何为 toString()
或 toInt()
创建通用函数?我在 Apple 文档中阅读了有关 extensions and generics 的信息,但我没有找到解决问题的方法。
最后,我尝试用相同的扩展扩展UIView
,因为它是UILabel
等的超类,但我不能调用getText()
。
那么创建通用扩展的好方法是什么?
对于第一个问题,您应该只扩展 UIView
并检查它是否是标签、文本字段或带有 if let
的文本视图。
extension UIView {
func getText() -> String {
if let label = self as? UILabel {
return label.text ?? ""
} else if let textField = self as? UITextField {
return textField.text ?? ""
} else if let textView = self as? UITextView {
return textView.text ?? ""
}
return ""
}
}
与创建协议相比,此方法的优势在于您可以轻松扩展它,即使 text/title 可以使用其他方法检索,例如 UIButton
.
[...]
else if let button = self as? UIButton {
return button.title(for: .normal) ?? ""
}
创建一个带有扩展的协议,然后扩展每个 class 以采用该协议:)
protocol GetTextProtocol {
var text: String? {get}
func getText() -> String
}
extension GetTextProtocol {
func getText() -> String {
return self.text ?? ""
}
}
extension UILabel: GetTextProtocol {
var text: String? {
return self.text
}
}
extension UITextView: GetTextProtocol {
var text: String? {
return self.text
}
}
这里有一些可疑的事情:当出现问题时,我不希望任何 toInt()
函数只是 return -1 或 0。 Swift 有一个很好的可选系统是有原因的,returning -1 或 0 引入了几个明显的陷阱。我也不知道你打算做什么来实现 getText()
在 UIView
上。许多视图没有文本。我不知道这个实现意味着什么或做什么。
所以我将忽略这两个细节并专注于主要问题,这似乎是您的扩展的冗余。有一种更简洁的方法,即通过协议扩展来减少重复。
protocol TextProviding {
var text: String? { get }
}
extension TextProviding {
// Following your example here. I would prefer calling
// this a `var textOrEmpty: String` but same idea.
func getText() -> String {
return text ?? ""
}
}
// To conform additional types, you just need 1 line
extension UILabel: TextProviding { }
extension UITextField: TextProviding { }
编辑:正如一些人指出的那样,将上面的代码与 extension UITextView: TextProviding { }
一起使用是行不通的,因为 UITextView
的 text
是 String!
,不是 String?
。不幸的是,这意味着如果您希望它也适用于 UITextView,您应该将 var text
要求重命名为其他名称(这意味着您将需要几行额外的代码来手动符合 UILabel
和 UITextField
).
protocol TextProviding {
var string: String? { get }
}
extension TextProviding {
var stringOrEmpty: String {
return string ?? ""
}
}
extension UILabel: TextProviding {
var string: String? { return text }
}
extension UITextField: TextProviding {
var string: String? { return text }
}
extension UITextView: TextProviding {
var string: String? { return text }
}
与此处发布的所有其他答案相比,one-liner 减少了冗余代码 (您想要的)。无需疯狂 condition-checks 或多个扩展。
extension UIView {
func getText() -> String? {
return self.responds(to: #selector(getter: UILabel.text)) ?
self.perform(#selector(getter: UILabel.text))?.takeUnretainedValue() as? String : nil
}
}