本文提供两种简单的AttributedString的生成方式
iOS中的NSAttributedString一直觉得不太好用,每次要加一个稍微复杂点的特征字符串就很麻烦,你可能会看到这样的代码
1 2 3 4 5 6 7 8 9
| let attrS = NSMutableAttributedString(string: "回忆是抓不到的月光,") let att1 = NSAttributedString(string: "握紧就变黑暗", attributes: [.font: UIFont.systemFont(ofSize: 20), .foregroundColor: UIColor.red ]) attrS.append(att1) attrS.append(NSAttributedString(string: ",让虚假的背影消失于晴朗,阳光在")) let attch = NSTextAttachment() attch.image = UIImage(named: "Lock") attch.bounds = CGRect(x: 0, y: -20, width: 50, height: 50) attrS.append(NSAttributedString(attachment: attch)) attrS.append(NSAttributedString(string: "身上流转,等所有业障被原谅"))
|
创建一个不太复杂的字符串就需要很长的代码,而且不是很直观。
为NSMutableAttributedString加拓展的方式
为了解决这个问题,笔者试用链式语法的方式对NSMutableAttributedString做了一个拓展,同时创建了一个Attributes的类。可以使用链式语法加闭包的方式快速简单的实现AttributedString,用了之后会再也回不去。
这里举个栗子,简单的方式就可以实现上述同样的方式。而且一些重复的工作一句就搞定
1 2 3 4 5 6 7
| let attr = NSMutableAttributedString() attr.add("回忆是抓不到的月光,") .add("握紧就变黑暗") {$0.font(20).color(.red)} .add(",让虚假的背影消失于晴朗,阳光在") .addImage("Lock", CGRect(x: 0, y: -20, width: 50, height: 50)) .add("身上流转,等所有业障被原谅") .add {$0.color(.blue)}
|
主要方法下三个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| @discardableResult func add(_ text: String, arrtibutes: ((inout Attributes) -> ())? = nil) -> NSMutableAttributedString { var arrtibute = Attributes() arrtibutes?(&arrtibute) let arrStr = NSMutableAttributedString(string: text, attributes: arrtibute.attributes) append(arrStr) return self } @discardableResult func add(arrtibutes: (inout Attributes) -> ()) -> NSMutableAttributedString { let range = NSRange(string.startIndex..<string.endIndex, in: string) var attribute = Attributes() arrtibutes(&attribute) enumerateAttributes(in: range, options: .reverse) { (oldAttribute, range, _) in var newAtt = oldAttribute attribute.attributes.forEach { (newkey, value) in if !oldAttribute.keys.contains(newkey) { newAtt[newkey] = value } } addAttributes(newAtt, range: range) } return self } @discardableResult func addImage(_ name: String, _ bounds: CGRect) -> NSMutableAttributedString { let attch = NSTextAttachment() attch.image = UIImage(named: name) attch.bounds = bounds let attchAttri = NSAttributedString(attachment: attch) append(attchAttri) return self }
|
1.func add(_ text: String, arrtibutes: ((inout Attributes) -> ())? = nil) -> NSMutableAttributedString
此方法为基础方法,添加字符串同时增加Attributes,对本段字符串生效,这里试用参数默认值的方式,如果只是添加字符串不加特征,可以省略闭包
2.func add(arrtibutes: (inout Attributes) -> ()) -> NSMutableAttributedString
我们也会遇到,一段字符串,中间几段特殊,然后其他的特征是一样的,这就可以使用这个方法。
该方法会对当前整体生效(调用该方法时候的字符串,不影响后序添加的字符),对已有的属性不影响,只会为不包含当前属性的添加该特征
3.func addImage(_ name: String, _ bounds: CGRect) -> NSMutableAttributedString
添加Image的支持
其中Attributes的类比较简单这里就不提了,可以直接看源码
使用字符串插值协议
最近又发现一个好玩的东西StringInterpolationProtocol,它可以让你使用\()的方式为字符串插值
下面这种形式你应该不陌生
1 2
| let time = 10 let string = "The time is \(time)."
|
使用\()的方式可以在字符串中方便的插值这个是很早就有的,而在swift5中,swift增加了StringInterpolationProtocol。这个协议允许你来以自己的方式来实现字符串插值。这就为特征字符串的生成方式提供了新的可能,这里不再详细介绍协议的用法,主要说说如何用来做特征字符串的实现,我们先来看结果:
1 2 3 4 5 6 7
| let str: AttributedString = "\("回忆", .weithtFont(20, .semibold), .color(.cyan), .stroke(.blue, 3))是抓不到的月光, \("握紧就变黑暗", .font(20), .color(.red), .paragraphStyle{$0.alignment(.center)}, .strike(.black, .double)),让 \("虚假", .font(20), .kern(5), .obliqueness(-0.2), .color(.orange))的背影消失于 \("晴朗", .font(20), .underline(.blue, .double)),阳光在 \(image: UIImage(named: "Lock"), bounds: CGRect(x: 0, y: -20, width: 50, height: 50)) 身上流转,等所有业障被原谅"
|

这里AttributedString是自己创建的类,主要方法是下面两个
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func appendInterpolation(_ string: String, _ attributes: AttributedString.Attributes...) { var attr = [NSAttributedString.Key : Any]() attributes.forEach { attr.merge($0.attributes, uniquingKeysWith: {$1})} let astr = NSAttributedString(string: string, attributes: attr) self.attributedString.append(astr) }
func appendInterpolation(image: UIImage?, bounds: CGRect) { let attch = NSTextAttachment() attch.image = image attch.bounds = bounds let attchAttri = NSAttributedString(attachment: attch) self.attributedString.append(attchAttri) }
|
appendInterpolation 也是这个协议中最主要的方法,每次有\()的时候就会走进你定定义的方法。后面的Attributes也是自定义的新的类,用来添加Attribtue这里使用了可变形参函数,可以传入多个Attributes
具体代码逻辑可以看这里
关于StringInterpolationProtocolSwift中有详尽的描述和例子,可以参考代码和文档理解