结果:
Swift 4.2 和 5.0:
首先,我们创建一个UILabel、UITextField 和UITextView 可以采用的协议。
public protocol ChangableFont: AnyObject {
var rangedAttributes: [RangedAttributes] { get }
func getText() -> String?
func set(text: String?)
func getAttributedText() -> NSAttributedString?
func set(attributedText: NSAttributedString?)
func getFont() -> UIFont?
func changeFont(ofText text: String, with font: UIFont)
func changeFont(inRange range: NSRange, with font: UIFont)
func changeTextColor(ofText text: String, with color: UIColor)
func changeTextColor(inRange range: NSRange, with color: UIColor)
func resetFontChanges()
}
我们希望能够为我们的文本添加多个更改,因此我们创建了rangedAttributes 属性。它是一个自定义结构,包含属性及其应用范围。
public struct RangedAttributes {
public let attributes: [NSAttributedString.Key: Any]
public let range: NSRange
public init(_ attributes: [NSAttributedString.Key: Any], inRange range: NSRange) {
self.attributes = attributes
self.range = range
}
}
另一个问题是UILabel 的font 属性很强大,而UITextField 的font 属性很弱/可选。为了使它们都与我们的ChangableFont 协议一起工作,我们包含了getFont() -> UIFont? 方法。这对于 UITextView 的 text 和 attributedText 属性也很重要。这也是我们为它们实现 getter 和 setter 方法的原因。
extension UILabel: ChangableFont {
public func getText() -> String? {
return text
}
public func set(text: String?) {
self.text = text
}
public func getAttributedText() -> NSAttributedString? {
return attributedText
}
public func set(attributedText: NSAttributedString?) {
self.attributedText = attributedText
}
public func getFont() -> UIFont? {
return font
}
}
extension UITextField: ChangableFont {
public func getText() -> String? {
return text
}
public func set(text: String?) {
self.text = text
}
public func getAttributedText() -> NSAttributedString? {
return attributedText
}
public func set(attributedText: NSAttributedString?) {
self.attributedText = attributedText
}
public func getFont() -> UIFont? {
return font
}
}
extension UITextView: ChangableFont {
public func getText() -> String? {
return text
}
public func set(text: String?) {
self.text = text
}
public func getAttributedText() -> NSAttributedString? {
return attributedText
}
public func set(attributedText: NSAttributedString?) {
self.attributedText = attributedText
}
public func getFont() -> UIFont? {
return font
}
}
现在我们可以通过扩展我们的协议继续为UILabel、UITextField 和UITextView 创建默认实现。
public extension ChangableFont {
var rangedAttributes: [RangedAttributes] {
guard let attributedText = getAttributedText() else {
return []
}
var rangedAttributes: [RangedAttributes] = []
let fullRange = NSRange(
location: 0,
length: attributedText.string.count
)
attributedText.enumerateAttributes(
in: fullRange,
options: []
) { (attributes, range, stop) in
guard range != fullRange, !attributes.isEmpty else { return }
rangedAttributes.append(RangedAttributes(attributes, inRange: range))
}
return rangedAttributes
}
func changeFont(ofText text: String, with font: UIFont) {
guard let range = (self.getAttributedText()?.string ?? self.getText())?.range(ofText: text) else { return }
changeFont(inRange: range, with: font)
}
func changeFont(inRange range: NSRange, with font: UIFont) {
add(attributes: [.font: font], inRange: range)
}
func changeTextColor(ofText text: String, with color: UIColor) {
guard let range = (self.getAttributedText()?.string ?? self.getText())?.range(ofText: text) else { return }
changeTextColor(inRange: range, with: color)
}
func changeTextColor(inRange range: NSRange, with color: UIColor) {
add(attributes: [.foregroundColor: color], inRange: range)
}
private func add(attributes: [NSAttributedString.Key: Any], inRange range: NSRange) {
guard !attributes.isEmpty else { return }
var rangedAttributes: [RangedAttributes] = self.rangedAttributes
var attributedString: NSMutableAttributedString
if let attributedText = getAttributedText() {
attributedString = NSMutableAttributedString(attributedString: attributedText)
} else if let text = getText() {
attributedString = NSMutableAttributedString(string: text)
} else {
return
}
rangedAttributes.append(RangedAttributes(attributes, inRange: range))
rangedAttributes.forEach { (rangedAttributes) in
attributedString.addAttributes(
rangedAttributes.attributes,
range: rangedAttributes.range
)
}
set(attributedText: attributedString)
}
func resetFontChanges() {
guard let text = getText() else { return }
set(attributedText: NSMutableAttributedString(string: text))
}
}
在默认实现中,我使用一个小助手方法来获取NSRange 的substring。
public extension String {
func range(ofText text: String) -> NSRange {
let fullText = self
let range = (fullText as NSString).range(of: text)
return range
}
}
我们完成了!您现在可以更改部分文本的字体和文本颜色。
titleLabel.text = "Welcome"
titleLabel.font = UIFont.systemFont(ofSize: 70, weight: .bold)
titleLabel.textColor = UIColor.black
titleLabel.changeFont(ofText: "lc", with: UIFont.systemFont(ofSize: 60, weight: .light))
titleLabel.changeTextColor(ofText: "el", with: UIColor.blue)
titleLabel.changeTextColor(ofText: "co", with: UIColor.red)
titleLabel.changeTextColor(ofText: "m", with: UIColor.green)