开源学习之StyledTextKit
StyledTextKit是一个简单的设置属性字符串的工具,使用builder模式构建,加入了上下文的概念,使得在创建属性字符串的时候能减少很多样板代码。
一个例子
先从使用入手,以下展示了如何创建一个属性字符串,其中Foo和baz!的样式是一样的,bar不同于他们:

其中使用到了StyledTextBuilder
这个build类,它可以接收如下几种类型的参数,最终结果都是为数组变量styledTexts
赋值。

接下来是save
,这个方法是保存当前的环境,以便提供新的样式:
1 | @discardableResult |
通过add这个函数来添加新的文本
以及样式
,和初始化一样,add操作也支持几种类型的参数,包括string
、image
、attributedString
以及该框架的StyledText类
,他们这些方法最后都会将参数映射成StyledText
1 | @discardableResult |
但是他们最终都是调用的这个函数:

这个函数内部的-add…
方法会根据参数最后调用-add(styledTexts:)
函数,为styledTexts数组
添加一个元素。
与save成对的是restore
,它可以跳出当前的样式上下文:
1 | @discardableResult |
接着是build,build函数会根据初始化
、添加的styledTexts数组
生成一个StyledTextString
对象。
1 | public func build(renderMode: StyledTextString.RenderMode = .trimWhitespaceAndNewlines) -> StyledTextString { |
在StyledTextString内部会遍历传入的styledTexts数组,在外部调用render
方法的时候生成对应的NSAttributedString:

以上就是这个工具的简单使用,到现在为止,涉及到了以下几个类:
- StyledTextBuilder
- TextStyle(结构体)
- Storage(枚举)
- StyledText
- StyledTextString(结构体)
简单的使用了下,发现它对于类的命名很乱,很多类容易产生歧义或者容易混淆。
StyledText
这个框架中提供了两个重要的角色:Builder
、Render
,他们根据文本或者图片进行构建然后渲染成属性字符串,这个过程可以用下面的图进行概括:

Builder
这个角色的抽象是StyledTextBuilder
,主要负责对接业务方,比如为哪一些字符串、哪一个image设置对应的属性。主要的操作是为其中的styledTexts数组
添加对应的StyledText
元素,以及通过一个save-restore操作来修改对应的style。

styledTexts数组中的StyledText元素通过将数据和样式分离,抽象出来Storage枚举
和TextStyle类
,这样处理起来会更加的灵活。这个数组是builder用来传递给下一个Render的重要数据,至于另外的一个savedStyles只不过是添加了一个save-restore特性,最终结果还是会作用到styledText数组中。
savedStyles会被用在两个api中:save()
、restore()
。想象一个场景,包含姓名的字符串中,要将姓名做不同颜色、不同字体等处理,这个时候就可以使用save-restore这一个成对的api来完成,save之后执行add添加的字符串具备自己的样式,直到restore为止。
Render
他的抽象类是StyledTextString
,需要通过builder传递过来装有StyledText的数组,然后根据设置的渲染模式
渲染出来对应的属性字符串。

Render生成属性字符串的方法是render(contentSizeCategory:)
,根据styledTexts数组
生成对应的属性字符串,然后根据自己的renderMode对这个字符串进行相应的修改,renderMode暂时可以不用考虑,只是作者考虑多情形的时候提供的功能。
由于styledTexts数组
中装的是数据和样式的抽象
:StyledText,因此,可以很快的生成对应的属性字符串,这一步是在StyledText的render(contentSizeCategory:)
函数中完成的。styledTexts数组
中的元素StyledText依次通过render方法生成的NSAttributedString会依次的追加到一个NSMutableAttributedString中,这个可变属性字符串就是业务方需要的最终结果。
可以看到,
- 设置数据和样式并存储是在Build阶段完成的,也就是生成一系列的StyledText对象;
- 渲染出对应的属性字符串是在Rende阶段完成的,根据一系列的StyledText对象依次执行render方法得到属性字符串。
Add
从上面的使用例子中大致理出来add的一系列方法之间的关系如下图所示,这些add方法的作用是为Builder的styledTexts数组
增加新的元素,styledTexts数组会在之后的render阶段中使用。

这些add方法中最重要的是add(storage:)
这一个,他会根据不同类型的数据来生成一个StyledText对象
,通过源码发现内部使用一个Storage枚举
和一个TextStyle对象
生成StyledText。Storage好说,就是函数的入参之一,后面TextStyle的创建才是这个函数的核心。
1 | @discardableResult |
Save-Restore
对一些特殊显示效果的字符串,框架提供的方式是一个成对的api:save()
、restore()
。
当执行save的时候,会从已经存在的styledTexts数组
中找到最后的一个StyledText对象的style,也就是TextStyle类型的元素,然后将它放入到savesStyles数组中。
而restore函数内部会将savesStyles数组中的最后一个元素作为生成StyledText的参数,然后将新生成的StyledText对象放入styledTexts数组
中,并且移除savesStyles数组
中的最后一个元素。这里使用数组来完成状态保存的功能是为了便于多个save一起操作。
在save和restore函数中间,可以像往常一样执行add操作,添加对应的数据。
other
该框架还另外提供了一个StyledTextView
,用来呈现生成的NSAttributedString,但是性能不是很高效,这里就没有对其进行进一步的探究了。
参考文章
- 参考文章列表