Swift 中 UI 元素的通用 `getText()` 函数

Generic `getText()` function for UI elements in Swift

很多动作需要检查label.texttextView.texttextField.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 ?? ""
    }
}

这些扩展非常多余。类似的情况是当我需要将 IntDoubleFloat 等转换为另一种数字格式时。我想要的是一个简单的 toInt(),当出现问题时,return 为 -10

那么如何为 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 { } 一起使用是行不通的,因为 UITextViewtextString!,不是 String?。不幸的是,这意味着如果您希望它也适用于 UITextView,您应该将 var text 要求重命名为其他名称(这意味着您将需要几行额外的代码来手动符合 UILabelUITextField).

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
    }
}