0%

使 DTCoreText 变得更好

DTCoreText 是一款 Github 上的开源库,可用于摆脱低性能的 WebView 却又以 WebView 的方式来展示 HTML。整体逻辑包含以下几个部分:

  1. 解析HTML标签、CSS,构建DOM树,创建出 NSAttributedString 

  2. 将 NSAttributedString 拆分、构建成每一行的 CTLine、继而拆分到 CTRun 

  3. 绘制每一个 CTRun 


然而虽然它一直在更新,但至今也存在一些可优化空间。接下来从功能、计算、实现上分别展开说说。

1. 实现圆角和边框

DT 在解析读取 css 时,也有简单地读取了边框颜色、宽度、圆角等数据。

奇怪的是最终进行绘制的时候却又没有将其读出来加以绘制。这里的改进方式也比较简单,在原本 draw 的位置判断下如果有圆角或边框,就执行额外的绘制方法将其画出来即可。但是对于圆角边框的绘制可能里面的逻辑稍微复杂,边框的宽度实际会影响进行实际要画出来的圆角弧线的半径。

例如 css 里配置了圆角半径R为10,边框宽度w为4,则按照此宽度画出来的圆弧的半径r实际应该是(R-w/2)==8。

同时要注意会存在w>R的情况,此时r只应取得最大值R/2,即画出1/4圆的情况。因此计算要绘制出来的圆弧圆角半径为:

1
2
r = R - w / 2 (w <= R)
r = R / 2 (w > R)

这个方案其实还不是很完善,更好的实现应该是能够实现四条边里每两条边的连接处是外边与外边相连,内边与内边相连的。但这里涉及到更复杂的代码量以及数学算法了,简单来做按照上面的方式就可以了。

2. 位置计算

DT 定义的 textBlock 里,记录了其 padding,却没有记录其 margin。继而在后续计算 block 的位置时也不会针对其具有的 margin 属性来做相应的偏移。致使进行背景色的填充等时候只能画出满屏的宽度而无法针对标签本身的位置来画。

此外,当标签文本配置通过了 justify 的方式来布局时,如果当前行因最后一个单词过长、或最后一行文字太少等原因而导致行末存在一段空白区域,则此时应对这行的字间距进行拉大来使显示效果更好。但 DT 在计算文本是否需要拉伸时并未依据文本的起始位置来计算是否应该进行拉伸,而是直接使用了满屏的宽度,从而使得那些实际显示不是满屏的文本没有进行合理的拉伸。

改进前

改进后

这2点的改进方案都比较简单,就不多说了。

3. 底层实现

DT 是依据每一段 string 来找出其中所含的属性来进行绘制的。例如画背景时,是先找出当前这段字符串,再从这段字符串里找到里面所具有的属性(背景色),然后将其绘制出来。而如果存在一个 div 里面不包含任何的文案,而却有背景色时,则这个背景色将无法被绘制出来。

同时上面提及到的一切绘制,都需要依赖 textBlock 这个结构体,而这个结构体却只会在标签的 display 类型是 block 时才会被创建出来。从而 <span> 等标签是无法绘制出背景色的。

因此 textBlock 不应只应用在 “block” 里、没有文本的标签里或许也可以主动往里插入一个不可见的字符,或者甚至是将结构改一下,不再是 string 里包含 textBlock,而是 textBlock(或者其他名称)里包含 string,应该能解决这些问题了。