在iOS开发中,处理文本的视图控件主要有4中,UILabel,UITextField,UITextView和UIWebView。其中UILabel与UITextField相对简单,UITextView是功能完备的文本布局展示类,通过它可以进行复杂的富文本布局,UIWebView主要用来 加载网页或者pdf文件,其可以进行HTML,CSS和JS等文件的解析。
TextKit是一个偏上层的开发框架,在iOS7以上可用,使用它开发者可以方便灵活处理复杂的文本布局,满足开发中对文本布局的各种复杂需求。TextKit实际上是基于CoreText的一个上层框架,其是面向对象的,如果TextKit中提供的API无法满足需求,可以使用CoreText中的API进行更底层的开发。
官方文档中的一张图片很确切,经常会被用来描述TextKit框架在iOS系统文本渲染中所处的位置。
界面在进行文本的渲染时,有下面几个必要条件:
1.要渲染展示的内容。
2.将内容渲染在某个视图上。
3.内容渲染在视图上的尺寸位置和形状。
在TextKit框架中,提供了几个类分别对应处理上述的必要条件:
1.NSTextStorage对应要渲染展示的内容。
2.UITextView对应要渲染的视图。
3.NSTextContainer对应渲染的尺寸位置和形状信息。
除了上述3个类之外,TextKit框架中的NSLayoutManager类作为协调者来进行布局操作。
上述关系如下图所示:
前面都是一些基本介绍,直接摘录云栖社区大神的,TextKit主要用于更精细的处理文本布局以及进行复杂的图文混排布局,使用TextKit进行文本的布局展示十分繁琐,首先需要将显示内容定义为一个NSTextStorage对象,之后为其添加一个布局管理器对象NSLayoutManager,在NSLayoutManager中,需要进行NSTextContainer的定义,定义多了NSTextContainer对象则会将文本进行分页。最后,将要展示的NSTextContainer绑定到具体的UITextView视图上。也就是说我们可以通过使用TextKit完全的控制每一个字形的展示位置以及样式。
下图是他们的之间关系:
下面根据这张图谈一谈我的理解:NSTextStorage对象保存着要展示的内容,文字,图片。我认为最重要的一个类是NSLayoutManager,他管理着每一个字符的显示。NSTextContainer其实是管理着每一行字形的布局之后通过绑定TextView一行一行的绘制展现出来文字的,当然底层渲染使用的都是coreText。
这样我们是不是可以通过修改NSTextContainer显示区域,来改变文本的显示相撞,比喻显示为一个三角形、圆形等。答案当然是可以的,我们需要重写NSTextContainer里面的
override func lineFragmentRect(forProposedRect proposedRect: CGRect, at characterIndex: Int, writingDirection baseWritingDirection: NSWritingDirection, remaining remainingRect: UnsafeMutablePointer<CGRect>?) -> CGRect
这个方法是什么意思呢?官方文档那个解释:就是返回每行文本绘制的rect,这样我们改变rect的point就可以实现圆形文本了。
override func lineFragmentRect(forProposedRect proposedRect: CGRect, at characterIndex: Int, writingDirection baseWritingDirection: NSWritingDirection, remaining remainingRect: UnsafeMutablePointer<CGRect>?) -> CGRect {
let size = self.size
let rect = super.lineFragmentRect(forProposedRect: proposedRect, at: characterIndex, writingDirection: baseWritingDirection, remaining: remainingRect)
let radius = fmin(size.width, size.height)/2
let ypos = fabs((rect.origin.y + rect.size.height/2) - radius)
let width = (ypos < radius) ? 2 * sqrt(radius * radius - ypos * ypos) : 0.0
let finalRect = CGRect.init(x: rect.origin.x + radius - width/2.0, y: rect.origin.y, width: width, height: rect.size.height)
return finalRect
}
这样我们把我们textView的container替换为我们自定义的,就可以实现一个圆形的文本。
当然我们也可以去修改NSLayoutManager,现在我们每一行文字都是整齐的排列,那么我们可以修改每个字符的位置,让其斜线排列。
override func draw(_ rect: CGRect) {
// 获取对应的字型的位置
var range = NSRange.init(location: 0, length: 0)
_ = layoutManager.lineFragmentRect(forGlyphAt: 0, effectiveRange: &range)
for glyIndex in range.location ... (range.location + range.length - 1) {
let point = layoutManager.location(forGlyphAt: glyIndex)
print(point)
// 绘制文字
self.layoutManager.drawGlyphs(forGlyphRange: NSRange.init(location: glyIndex, length: 1), at: CGPoint.init(x: 10, y: 20 * glyIndex))
}
}
TextKit的大概使用就是这样的,NSLayoutManager里面很多方法,灵活运用可以做出很酷炫的效果。