如何使用Core Text计算一段文本绘制在屏幕上之后的高度
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明。
http://blog.devep.net/virushuo/2010/07/17/cocoa-core-text-text-height.html
Core Text提供了一系列方便的函数,可以很容易的把文本绘制在屏幕上,对于一个Frame来说,一般并不需要担心文本的排列问题,这些Core Text的函数都可以直接搞定,只要给他一个大小合适的CGRect就可以。
但,在某些情况下,我们还希望知道这段文本在绘制之后,对应绘制的字体字号设置,在屏幕上实际占用了多大面积。举例来说,有文本段落a,屏幕大小rect,通常做法是以rect创建path,然后创建CTFramesetter,再然后创建CTFrame,最后用CTFrameDraw画出来,这时候,往往文本段落占用的实际面积会小于rect,这时候就有必要获得这段文本所占用的真正面积。
最理想的情况是使用
double CTLineGetTypographicBounds( CTLineRef line, CGFloat* ascent, CGFloat* descent, CGFloat* leading );
这是Core Text提供的函数,传入CTLine,就会得到这一行的ascent,descent和leading,在OSX上通常可以工作的很好,但是在iOS(iPhone/iPad)上这个函数的结果略有不同。
正常情况下,计算行高只需要ascent+descent+leading即可。在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距,ascent+descent计算出来的Glyph的高度还是正确的。
这样就有了第一步
在创建用于绘图的CFAttributedStringRef时,除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了。
但是这还是有问题,因为OSX上和iOS处理不同,所以事实上iOS的模拟器和真机的最终效果是不一样的,这样调试程序很麻烦。
于是还需要第二步
在最终往页面上绘制的时候,不再用CTFrameDraw来一次绘制全部,而是使用CTLineDraw逐行画,在画之前,先用CGContextSetTextPosition来设置好每行文本的位置。这样就保证了在任何平台上绘制效果一致。
问题就解决了。
CoreText在OS X和iOS上实现有很多细节区别,比如说,对CTRun的划分方式也不一样,在iOS上划分出来的Run数量比OSX上少很多,是按照字体划分的Run,按照文档出来,这是正确的,但OSX上是一个字一个Run这样划分的,和文档不符。iOS上的处理显然效率更好。
stackoverflow 上有一个类似的讨论,不过没考虑到iOS的区别





Comments
沙发:)
Posted by: lispython | July 19, 2010 1:27 AM
这文不错!
Posted by: 阅网博客 | July 20, 2010 1:21 AM
如果你传入的是一段比较长的文本,一般可以传入一个如一页大小的 CGRect, 在创建完 CTFrameRef 之后再用 CTFrameGetVisibleStringRange() 获得该 frame 的文本范围,然后再用这个范围以后的内容创建下一个 CTFrameRef。
suggest frame size 确实是不够准确的,这个在 Core Text 邮件列表上也有 Apple 开发者解释过。
Posted by: jjgod | August 3, 2010 11:50 PM
另外“但OSX上是一个字一个Run这样划分的”这个说法不知道你从何得来?根据我个人的经验不是这样的,在 vim-cocoa 中我是用 Core Text 渲染文本的,其中 CTRun 的分隔是正确按照字符属性和使用的字体来划分的,不会出现你说的“一个字一个Run”的情况。
Posted by: jjgod | August 3, 2010 11:52 PM
@jjgod
CTFrameGetVisibleStringRange 可以,但在我说的这种情况下高度计算是有错的。这个现象在不同的系统上不一样。暂时还没搞清原因。如果用整个页面做CGRect,这个问题不太明显,如果做图文混拍,就会看到几个区域有时候会重叠。目前只有我用的这个办法可以计算准确。
目前发现的情况:在iMac和mbp上,是正确的。但在macbook和iPad上是错误的。
一个字一个字run的情况出现在iMac上。
不然你以为为什么会有stackoverflow那个讨论,这个问题很多人碰上,有时间你可以试试看。
Posted by: virushuo | August 4, 2010 11:41 AM
不懂这方面,没有用过
Posted by: 阿飞 | August 21, 2010 8:26 PM
welcome to our website http://www.pop-nfljerseys.com .We are a professional company, and engaged in the nfl sales, we sell us football jerseys,and our nfl shop also sell basketball jersey, baseball jerseys, soccer jerseys and so on, also our company belongs to authentic wholesale.
Posted by: nfl sales | August 21, 2010 10:42 PM
不懂这些语言。
Posted by: 光大证券下载 | August 23, 2010 12:02 PM
学习了,谢谢博主分享
Posted by: 汽车导航仪哪个好 | August 30, 2010 6:09 PM
It seems like e very good web site but my Chinese is not good. It would be great if it might be available in English too. Thanks.
Posted by: Yurtdışı Eğitim | September 2, 2010 1:48 AM