记得上次更新是半年前了,转眼就到了18年的Q3了。回顾一下我的博客的空窗期,记得在年初的时候前东家孵化了个线上的视频约见的项目,自己也算是尽力在做。之后又对项目管理感兴趣学了些报了个项目管理的考试,然而在最后一门论文崩盘也是心塞。在工作学习中发现,自己接触的越多越会感觉到自己的渺小自己的不足,同时考完试之后跳了一家更大的公司也搬了家,在项目组里面默默无闻,尽力去做的更好去完善自己吧,咳咳,扯多了,作为回归的第一篇还是得有点质量,总结一下CoreText的相关的东西,尽量用最少的文字讲更多的东西
Core Text
OverView
Core Text提供了一个底层的编程接口,用于文本的布局和处理字体。Core Text布局引擎旨在实现高性能,易用性以及与Core Foundation的紧密集成。文本布局API提供高质量的排版,包括字符到字形的转换,带有连字,字距调整等。互补的Core Text字体技术提供自动字体替换,字体描述符和集合,轻松访问字体度量和字形数据以及许多其他功能。
多线程中的注意事项:Core Text中的所有单个函数都是线程安全的。字体对象(CTFont,CTFontDescriptor和相关的对象)可以被多个任务、队列或线程同时使用。但是,布局对象(CTTypesetter,CTFramesetter,CTRun,CTLine,CTFrame和关联对象)应该在单个操作,工作队列或线程中使用。
基本概念
坐标系相关
我们如果不做任何转换的话我们应该绘制的是这个样子的
可能图有点小,大致就是y轴反向的样子,因为Core Text
期初是OS X
系统上面的底层库,y轴为向上的方向,原点坐标是在左下,而iOS
的原点坐标是在左上角的,y轴正好是反方向
Core Text相关概念
今天我们尽量不去涉及Text Kit相关的东西,我们只是借用一下图,Text Kit是对Core Text的更高级的封装。
如下图我们可以了解到Core Text的形成层次结构,最上层的是CTFramesetter
工厂对象CTFramesetterRef
可以理解为负责一个区域的图文绘制的对象,随后这个工厂对象会根据属性字符串去调用CTTypesetter
对象去做排版相关的操作,同时根据属性字符串CTFramesetter
对象生成若干CTFrame
段落对象,每个段落对象又生成若干的CTLine
对象可以理解为每个段落中的行的对象,最后每个CTLine
行对象中又生成若干CTRun
字形运行时对象,每个对象里面的每个字形都有相同的属性,比如一行6个字里面cccccc
,每个字母都不一样的颜色那这个CTLine
对象就有6个CTRun
对象
基本Class
CTFont
CTFont
用来表示Core Text
字体对象。字体对象表示应用程序的字体,CTFont
提供对字体描述的访问,例如磅值,变换矩阵属性和其他属性。字体提供了相对于彼此布置字形的帮助,并用于在图形上下文中绘制时设置字体。
CTFontCollection
CTFontCollection
用来表示字体描述对象集合,即一组字体合成的单个对象。
CTFontDescriptor
CTFontDescriptor
用来表示字体描述对象,可以指定字体的属性字典(例如名称,磅值和变体)。
CTFrame
CTFrame
用来表示包含多行文本的框架。帧对象是由框架集对象执行的文本框架处理产生的输出。
CTFramesetter
CTFramesetter
类型用于生成CTFrame对象。CTFramesetter是CTFrame对象的对象工厂。
CTGlyphInfo
CTGlyphInfo
类型让你可以覆盖字体从Unicode到字形ID的指定映射。
CTLine
CTLine
用来表示一行文本。
CTParagraphStyle
CTParagraphStyle
用来表示属性字符串中的段落或标尺属性对象。
CTRun
CTRun
用来表示字形运行,它是一组拥有相同属性和方向的连续字形。
CTRunDelegate
CTRunDelegate
用来表示一个运行代理,其被分配到一个运行(属性范围),以控制印刷性状例如字的向上偏移量,字的向下偏移量,和字宽。
CTTextTab
CTTextTab
用来表示段落样式中的选项卡,存储对齐类型和位置。
CTTypesetter
CTTypesetter
用来表示排版器,它执行行布局。
Demo
简单的绘制文字
因为Core Text
底层是使用Core Graphics
来绘制的,我们要在UIView
的drawRect
中写相关的代码
1 | //1 |
代码详解
1
应该不用过多的解释,就是因为drawRect
方法内部是由Core Graphics
来做绘制任务的,再次可以取到当前绘制的上下文对象CGContextRef
。虽然在其他的地方可以自己去创建CGContextRef
上下文对象,然后自己去绘制,然后再释放,但系统在drawRect
绘制方法的时候会压栈进去一个系统创建的CGContextRef
上下文对象,我们为什么不去用呢,毕竟写Core Graphics
的代码也是很刺激,如果动态性很强建议不要在drawRect
中重写相关的代码,因为一旦重写了这个方法系统会自动给你分配一块内存去做绘制任务,大家可以使用断点打印一下self.layer.contents
,当没有实现drawRect
方法的时候是nil
咳咳,跑题了,大家可以看下面两个博客简单了解一下
1 | CGContextRef context = UIGraphicsGetCurrentContext(); |
2
这里我们可以对绘制区域做更细化的限制,如果Core Graphcis
用着不习惯就用UIBezierPath
来限制绘制区域吧,请注意这里面使用的仍然是系统坐标,如果需要绘制被排除的区域,需要使用系统坐标
1 | //初始化绘制的区域 |
3
坐标系相关概念如果还不是很理解可以去前面再看一下
1 | //设置字形的变换矩阵不做图形变换 |
4
不解释
5
1 | //初始化framesetter工厂对象 |
6
这也有多种写法,如果喜欢用UIBezierPath
也可以
1 | CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, [att length]), path , NULL); |
我们可以将 2 和 6 这样写
1 | UIBezierPath * path = [UIBezierPath bezierPathWithRect:self.bounds]; |
7.1
这个时候绘制区域对象已经准备好了,如果没有其他操作的话就可以直接绘制到self.layer
上面了
1 | CTFrameDraw(frame, context); |
7.2
但是有的时候我们需要对不同的行做操作,这个时候我们就要对CTLine分别做绘制了
1 | //获取CTLine数组 |
7.3
同时我们有的时候要对每行的CTRun
对象做不同的操作
我们将7.2的最后的操作换成下面的代码就是遍历每行的CTRun
进行绘制
1 | CFArrayRef runs = CTLineGetGlyphRuns(line); |
8
值得一提的就是,之前向系统申请的Context
不需要释放,系统会在恰当的时机去释放
1 | CFRelease(path); |
简单介绍一下概念,具体项目中可以用到的功能持续更新~
随后我们会讲到图文混排、文字点击回调、文字点击高亮等相关操作
英文水平贼渣的我看了好久英文文档和博客总结的,如果转载请附上链接https://coderwong.com/2018/07/05/CoreText/,万分感谢