编辑:Bison
投稿:大石头布
"CoreText是一个进阶的比较底层的布局文本和处理字体的技术,CoreText API在OS X v10.5 和 iOS3.2时引入,在OS X 和iOS 环境下均可以使用。:"
不是特别复杂的需求一般情况下UILabel、UITextView都可以搞定,他们是Apple帮我们封装好的显示文本的控件,但是像复杂的图文,链接识别并替换成 “点击链接” , @someone 绑定 ,电话识别,文字大小不一 ,当然这些可以使用UIWebView,但是CoreText 技术相对于 UIWebView,有着更少的内存占用,以及可以在后台渲染的优点,非常适合用于内容的排版工作。CoreText 提供了非常高的灵活性,但是操作起来的比较复杂,学技术不就应该找最难的攻克吗?
来看一张框架图
要学习CoreText 首先得了解属性字—NSAttributedString 或者 NSMutableAttributedString
NSAttributedString 和 NSMutableAttributedString
NSAttributedString是一个带有属性的字符串,通过该类可以灵活地操作和呈现多种样式的文字数据
可以事先定义好属性 然后加到文字上
效果
可以看到我们并没有给label设置颜色和字体,创建了一个带两个属性的NSAttributedString , 没有使用label.text而是 label.attributedText
如果只能给所有的文字设置一样的属性,那这个属性字也太没劲了。我们可以给一段文字设置不同的属性
我们可以给不同位置的文字指定不同的样式,位置通过NSRange给出(NSRange是一个结构体,有两个参数 一个location 一个 length ,这两个参数可以唯一的确定一段字串)
效果
这样我们就给前面的文字设置了两种不同的属性。
那么我们可以设置哪些属性呢。
-
NSFontAttributeName 设置字体属性,默认值:字体:Helvetica(Neue) 字号12
-
NSForegroundColorAttributeName 设置字体颜色,取值为 UIColor对象,默认值为
-
NSBackgroundColorAttributeName 设置字体所在区域背景颜色,取值为 UIColor对象,默认值为nil, 透明
-
NSLigatureAttributeName 设置连体属性,取值为NSNumber 对象(整数),0 表示没有连体字符,1 表示使用默认的连体字符
-
NSKernAttributeName 设定字符间距,取值为 NSNumber 对象(整数),正值间距加宽,负值间距变窄
-
NSStrikethroughStyleAttributeName 设置删除线,取值为 NSNumber 对象(整数)
-
NSStrikethroughColorAttributeName 设置删除线颜色,取值为 UIColor 对象,默认值为黑色
-
NSUnderlineStyleAttributeName 设置下划线,取值为 NSNumber 对象(整数),枚举常量 NSUnderlineStyle中的值,与删除线类似NSUnderlineColorAttributeName 设置下划线颜色,取值为 UIColor 对象,默认值为黑色
-
NSStrokeWidthAttributeName 设置笔画宽度,取值为 NSNumber 对象(整数),负值填充效果,正值中空效果
-
NSStrokeColorAttributeName 填充部分颜色,不是字体颜色,取值为 UIColor 对象NSShadowAttributeName 设置阴影属性,取值为 NSShadow 对象
-
NSTextEffectAttributeName 设置文本特殊效果,取值为 NSString 对象,目前只有图版印刷效果可用:
-
NSBaselineOffsetAttributeName 设置基线偏移值,取值为 NSNumber (float),正值上偏,负值下偏
-
NSObliquenessAttributeName 设置字形倾斜度,取值为 NSNumber (float),正值右倾,负值左倾
-
NSExpansionAttributeName 设置文本横向拉伸属性,取值为 NSNumber (float),正值横向拉伸文本,负值横向压缩文本
-
NSWritingDirectionAttributeName 设置文字书写方向,从左向右书写或者从右向左书写
-
NSVerticalGlyphFormAttributeName 设置文字排版方向,取值为 NSNumber 对象(整数),0 表示横排文本,1 表示竖排文本
-
NSLinkAttributeName 设置链接属性,点击后调用浏览器打开指定URL地址
-
NSAttachmentAttributeName 设置文本附件,取值为NSTextAttachment对象,常用于文字图片混排
-
NSParagraphStyleAttributeName 设置文本段落排版格式,取值为 NSParagraphStyle 对象
这么多属性使用的也记不住,使用的时候自行google用法,还是很强大的😄!!
属性字就介绍这么多,简单富文本使用它就能搞定。下面介绍我们的主角–CoreText
###CoreText
属性字是用来给文本设置样式,那么CoreText就是用来给文本进行排版的,可以自定每行高度,每个字符占位 等等
CoreText 是用于处理文字和字体的底层技术。它直接和 Core Graphics(又被称为 Quartz)打交道。Quartz 是一个 2D 图形渲染引擎。Quartz 能够直接处理字体(font)和字形(glyphs),将文字渲染到界面上,它是基础库中唯一能够处理字形的模块。因此,CoreText 为了排版,需要将显示的文本内容、位置、字体、字形直接传递给 Quartz。相比其它 UI 组件,由于 CoreText 直接和 Quartz 来交互,所以它具有高速的排版效果。
我们来看一个CoreText对象模型图
来一段枯燥的讲解(后面会有🌰的)
如上图所述,其中Framesetter对应的类型是CTFramesetter,通过CFAttributedString(NSAttributeString 也可以无缝桥接)进行初始化,它作为CTFrame对象的生产工厂,负责根据path生产对应的CTFrame。CTFrame是可以通过CTFrameDraw函数直接绘制到context上的,当然你可以在绘制之前,操作CTFrame中的CTLine,进行一些参数的微调。CTLine 可以看做Core Text绘制中的一行的对象 通过它可以获得当前行的line ascent,line descent ,line leading,还可以获得Line下的所有Glyph Runs。CTRun 或者叫做 Glyph Run,是一组共享相同attributes(属性)的字形的集合体。CTFrame是指整个该UIView子控件的绘制区域,CTLine则是指每一行,CTRun则是每一段具有一样属性的字符串。比如某段字体大小、颜色都一致的字符串为一个CTRun,CTRun不可以跨行,不管属性一致或不一致。通常的结构是每一个CTFrame有多个CTLine,每一个CTLine有多个CTRun。
由于CoreText一开始便是定位于桌面的排版系统,所以使用了传统的原点在左下角的坐标系,所以它在绘制文本的时候都是参照左下角的原点进行绘制的。
如果你啥也不做处理,直接在这个context上进行CoreText绘制,你会发现文字是镜像且上下颠倒。
🌰来啦!先来看一个最简单的CoreText使用的例子
一个简单的例子
首先新建一个CTView继承自UIView
然后把这个view加在ViewController上
运行效果:
###解释:
1、 通过UIGraphisGetCurrentContext()获取当前的环境
2、将坐标系上下翻转。对于底层的绘制引擎来说,屏幕的左下角是(0, 0)坐标。而对于上层的 UIKit 来说,左上角是 (0, 0) 坐标。所以我们为了之后的坐标系描述按 UIKit 来做,所以先在这里做一个坐标系的上下翻转操作。翻转之后,底层和上层的 (0, 0) 坐标就是重合的了。
3、创建绘制区域CGPathCreateMutable(),CoreText 本身支持各种文字排版的区域,我们这里简单地将 UIView 的整个界面作为排版的区域。
当然这里如果觉得CGMutablePath 不好用 可以选择使用更方便的UIBezierPath来操作排版区域.
把上文中的3改成
把4改成 顺便给文字加了点属性
排版区域就变了 而且使用了UIBezierPath的API
4、根据AttributedString生成CTFramesetterRef,根据framesetter和绘图区域创建CTFrame
5、使用CTFrameDraw进行绘制(后面复杂的可能不会直接画frame 而是选择一行一行的画 )
图文混排
CoreText如果只能定义文本绘制区域,那就太没劲了,CoreText还可以支持图文混排,本地图片和网络图片。
CoreText从绘制纯文本到绘制图片,依然是使用NSAttributedString,只不过图片的实现方式是用一个空白字符作为在NSAttributedString中的占位符,然后设置代理,告诉CoreText给该占位字符留出一定的宽高。最后把图片绘制到预留的位置上。
图中 第一个图片是存在本地,第二个图片是来自网络。下面看看怎么做。代码有点长 但是思路应该清晰。
1、2、3、4和前面简单Demo是一模一样的 ,只是加了个行间距。
5、 这块用一个回调设置了图片大小等信息 ,然会创建了一个CTRun的代理,创建一个空白占位字符,给它加了个属性,后面绘制的时候好识别。 最后把这个占位符加到我们属性文本的某个位置。
地下网络图片 name换成了url 其他如法炮制
6、7和上小结一样的
8、 正式开始处理图片部分
根据CTFrame 获取 CTLine 获取 originsArray 每一行的原点,用来定位 。根据CTLine获取到 CTRun 。 CTRun是每一个相同属性字符串 ,但是不会隔行。
遍历CTRun 根据我们前面设置的属性 找到本地图片进行绘制
网络图片的绘制也很简单如果没有下载 先放个灰色的图占位,然后去下载,下载好了 赋值给self的一个变量 ,然后重绘就OK了 关于NSURLSession不会使用的可以看我的另一篇文章。NSURLSession
关于CoreText还有很多,逐行排版 文字和emoji 混排问题 , 连接识别 ,点击图片 点击连接等等。。篇幅有点长。。下一篇接着介绍,这篇先到这边。
本文实例代码已上传github: https://github.com/smalldu/ZZCoreTextDemo
博主app上线啦,快点此来围观吧
更多经验请点击
好文推荐:iOS开发之详解连连支付集成