iOS 方便的AttributedString生成

本文提供两种简单的AttributedString的生成方式

  iOS中的NSAttributedString一直觉得不太好用,每次要加一个稍微复杂点的特征字符串就很麻烦,你可能会看到这样的代码

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,用了之后会再也回不去。
这里举个栗子,简单的方式就可以实现上述同样的方式。而且一些重复的工作一句就搞定

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)}//此方法只对整个字符串没有加color特征的部分生效

主要方法下三个

/// 添加字符串并为此段添加对应的Attribute
    /// - Parameters:
    ///   - text: 要添加的String
    ///   - arrtibutes: Attribute特征
    /// - Returns: self
    @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
    }
    
    
    /// 添加Attribute作用于当前整体字符串,如果不包含传入的attribute,则增加当前特征
    /// - Parameter arrtibutes: Attribute的DIc
    @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
    }
    
    /// 为AttributeString添加图片
    /// - Parameters:
    ///   - name: 图片名字
    ///   - bounds: 图片的bounds
    /// - Returns: 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,它可以让你使用\()的方式为字符串插值
下面这种形式你应该不陌生

let time = 10
let string = "The time is \(time)."

  使用\()的方式可以在字符串中方便的插值这个是很早就有的,而在swift5中,swift增加了StringInterpolationProtocol。这个协议允许你来以自己的方式来实现字符串插值。这就为特征字符串的生成方式提供了新的可能,这里不再详细介绍协议的用法,主要说说如何用来做特征字符串的实现,我们先来看结果:

//一行太长这里用回车做了分隔,实际是一句代码
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))
身上流转,等所有业障被原谅"
效果.png

这里AttributedString是自己创建的类,主要方法是下面两个

    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中有详尽的描述和例子,可以参考代码和文档理解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容