( 工作两年以来,除了最初的由零开始学习iOS编程外,后来的模式多是现用现学,以结果为导向。现在突然来了这么一遭,本人表示真心不适应。不过事儿的确是好事儿,只能慢慢适应了,毕竟我的写作水平还停留在毕设阶段。最近为了项目,接触了Core Text编程,就现学现卖一把了。)
一、 什么是Core Text?
Core Text是在iOS3.2、OSX10.5及以上版本中引入的文本布局及格式化引擎,设计初衷如下:
1、 完整、统一的文本布局和字体API集;
2、 高效易用;
3、 和Core Foundation、Core Graphics(Quartz)及Cocoa紧密关联;
4、 原生Unicode处理;
5、 64-bit应用程序支持;
6、 简洁、连贯且灵活的API设计;
7、 可预测的成本结构和合理分工。
总而言之,就是简单的事情简单做。
二、 为什么选择Core Text?
在UIKit中,我们通常使用UILabel和UITextView来显示文字,但是所有文字只能使用单一的字体,如果想改变文字颜色或者字体,它们就无能为力了。Core Graphics(Quartz)能够完成很多UIKit做不到的事,但是没有足够易用的接口。Core Text是结合并超越两者的产物,它使我们对文本布局、字体颜色大小、段落样式等等你想的到的想不到的方方面面具有完全的掌控,使用起来也非常便捷。
三、 如何使用Core Text?
Core Text提供了以下对象,对象名均以CT开头:
1、 CTFramesetter:使用CTTypesetter根据给定的NSAttributedString和CGPath生成CTFrame,NSAttributedString是Core Text得以完成如此多复杂效果的功臣;
2、 CTTypesetter:设置段落样式,比如换行、行间距等;
3、 CTFrame:代表一段文字,含有数个CTLine对象;
4、 CTLine:代表一行文字,含有数个glyph-run即CTRun对象;
5、 CTRun:具有相同属性的有序字形集合。glyph即字形,用来描述文字的图形形状,不同的字体或样式都有不同的字形,如图1所示。
图1 字形展示
图2很形象直观的展现了Core Text各对象间的包含关系,是一个比较完整的Core Text对象模型。
图2 CoreText对象模型
下面讲一下如何用程序实现简单的文本布局:
1、 初始化CGPath
CGMutablePathRef path= CGPathCreateMutable();
CGRect bounds =CGRectMake(10.0, 10.0, 200.0, 200.0);
CGPathAddRect(path,NULL, bounds);
2、 初始化NSAttributedString
CFStringRef string =CFSTR("Core Text");
CFMutableAttributedStringRefattrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), string);
此处可以添加想要的样式,字体、字形、颜色、字间距、行间距或者加个下划线神马的。
3、 生成CTFramesetter
CTFramesetterRefframesetter = CTFramesetterCreateWithAttributedString(attrString);
CTFrameRef frame =CTFramesetterCreateFrame(framesetter,CFRangeMake(0, 0), path, NULL);
然后就可以绘制了。另外,还可以结合touch事件实现其他功能,比如打开网址、播打电话等常用功能。
怎么实现图文混排呢?Core Text本身没有画图能力,但是它是一个出色的布局引擎,取巧的做法是为图像留出合适的位置,然后再自行绘图。此时会用到CTRunDelegate(如图3所示),通过设置CTRunDelegate回调方法可以获取CTRun的上下缘高度及宽度,然后设置成理想的参数为图像留白。
图3 CTRunDelegate
四、 Core Text完美吗?
完美只是美丽的愿景,Core Text也不例外。在使用中,我发现了以下问题:
1、 CTFramesetterCreateFrame函数计算出的CTFrame超出了给定bounds
这个问题困扰了我几个版本了,原以为使用Core Text万无一失呢。UITextView的漏洞在于计算的是一个高度,layout时可能因为换行或其他原因临时更改了高度。Core Text又有所不同,计算得到的宽度根本就已经超出限定范围。
解决方法是使用这个函数:double CTLineGetTypographicBounds( CTLineRef line, CGFloat* ascent,CGFloat* descent, CGFloat* leading ),输入一个CTLine对象,可以得到其ascent、descent和leading值,返回值是其宽度,ascent、descent和leading三者相加即为该行高度。图片留白部分的descent和leading均为0,为了确保行间距,可以人为设置成合适的数值。图4为基本的字形术语:
图4 字形术语
2、 字体与设定不符
以前通用的系统字体Helvetica在Core Text上居然栽了跟头,比非CoreText上绘制的要粗许多,这个问题通过设置其他字体解决了。
3、 NSDataDector检测链接耗时过多
- (void)enumerateMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range usingBlock:(void (^)(NSTextCheckingResult *result,NSMatchingFlags flags, BOOL *stop))block;
NSTextCheckingResult代表各种链接,时间地址电话网址自定义等。NSDataDector是一个专门配合NSTextCheckingResult检测文本是否是特定链接的类,继承于NSRegularExpression类,但是这个函数耗时过多。
解决方法是自己写网址和电话号码的正则表达式,能提速不少。
暂时只有这么多,欢迎大家交流学习啊!