如何使用CoreText
CoreText是需要自己处理绘制,不像UILabel等最上层的控件 ,所以我们必须在drawRect中绘制,为了更好地使用,我们需要自定义一个UIView。
CoreText绘图流程:先创建一个framesetter——>创建path——>创建frame(携带framesetter和path等信息)——>生成上下文context——>CTFrameDraw绘制frame——>释放
转换坐标系
UIKit的坐标系原点是在右上角,CoreText的坐标原点是在左下角,并且绘制的内容是颠倒的,所以需要进行坐标转换,绘制的内容显示才能正常
781681-20160107095453075-2053394561.png.jpg#pragma mark - drawRect
- (void)drawRect:(CGRect)rect
{
// 1.获取图形上下文
CGContextRef context = UIGraphicsGetCurrentContext();
// 2.翻转坐标系
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(0, rect.size.height), 1.f, -1.f);
CGContextConcatCTM(context, transform);
// 3.获取CTFrameRef
self.frameRef = [self.attributedString prepareFrameRefWithRect:rect];
// 4.绘制高亮背景颜色
[self drawHighlightedColor];
// 5.一行一行的绘制文字
[self frameLineDraw];
// 6.绘制图片
[self drawImages];
}
一.关于上下文
由于绘制都是在Context上下文上进行的,所以先补充一些上下文的知识,上下文是什么?为什么要有上下文?上下文是怎么工作的?
Context上下文
上下文是什么:
上下文定义了我们需要绘制的地方。
为什么要有上下文:
要盛水,就需要有容器;要画画,就需要画板。上下文就是一个画板,在英语中词根con和com都有很多一起的意思,再看Context,就是很多文本在一起,这就是上下文了。
上下文是怎么工作的:
UIKit维护着一个上下文堆栈,UIKit 方法总是绘制到最顶层的上下文中。你可以使用 UIGraphicsGetCurrentContext() 来得到最顶层的上下文。你可以使用 UIGraphicsPushContext()和UIGraphicsPopContext()在 UIKit 的堆栈中推进或取出上下文。最为突出的是,UIKit 使用 UIGraphicsBeginImageContextWithOptions() 和 UIGraphicsEndImageContext()方便的创建类似于 CGBitmapContextCreate()的位图上下文。混合调用 UIKit 和 Core Graphics 非常简单:
二.主要函数
1、传入CTFrame,返回一个装有多个CTLine对象的数组。
CFArrayRef CTFrameGetLines( CTFrameRef frame ) CT_AVAILABLE(10_5, 3_2);
2、获取数组中的元素个数
CFIndex CFArrayGetCount(CFArrayRef theArray);
3、获取数组中第idx个元素
const void *CFArrayGetValueAtIndex(CFArrayRef theArray, CFIndex idx);
4、 获取所有CTLineRef的基础原点,传入CTFrame,CFRange,和一个CGPoint的结构体数组指针,该函数会把每一个CTLine的origin坐标写到数组里。
void CTFrameGetLineOrigins( CTFrameRef frame, CFRange range, CGPoint origins[] ) CT_AVAILABLE(10_5, 3_2);
5、获取CTLine中文字在整段文字中的Range
CFRange CTLineGetStringRange( CTLineRef line ) CT_AVAILABLE(10_5, 3_2);
6、获取CTLine中的CTRun的数组
CFArrayRef CTLineGetGlyphRuns( CTLineRef line ) CT_AVAILABLE(10_5, 3_2);
7、获取CTRun在整段文字中的Range
CFRange CTRunGetStringRange( CTRunRef run ) CT_AVAILABLE(10_5, 3_2);
8、 获取点击处position文字在整段文字中的index
CFIndex CTLineGetStringIndexForPosition( CTLineRef line, CGPoint position ) CT_AVAILABLE(10_5, 3_2);
9、获取整段文字中charIndex位置的字符相对line的原点的x值
CGFloat CTLineGetOffsetForStringIndex( CTLineRef line, CFIndex charIndex, CGFloat * __nullable secondaryOffset ) CT_AVAILABLE(10_5, 3_2);
10、获取数组中字形个个数
CFIndex CTLineGetGlyphCount( CTLineRef line ) CT_AVAILABLE(10_5, 3_2);
11、设置CoreText绘制前的坐标。设置基线位置
CG_EXTERN void CGContextSetTextPosition(CGContextRef __nullable c, CGFloat x, CGFloat y) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
12、绘制CTLine。
void CTLineDraw( CTLineRef line, CGContextRef context ) CT_AVAILABLE(10_5, 3_2);
13、获取CTLine的上行高度,下行高度,行距
double CTLineGetTypographicBounds( CTLineRef line, CGFloat * __nullable ascent, CGFloat * __nullable descent, CGFloat * __nullable leading ) CT_AVAILABLE(10_5, 3_2);
14、设置当前文本矩阵
CG_EXTERN void CGContextSetTextMatrix(CGContextRef __nullable c, CGAffineTransform t) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
15、获取一行文字的范围, 就是指把这一行文字点有的像素矩阵作为一个image图片,来得到整个矩形区域。相对于每一行基线原点的偏移量和宽高(例如:{{1.2, -2.57227}, {208.025, 19.2523}},就是相对于本身的基线原点向右偏移1.2个单位,向下偏移2.57227个单位,后面是宽高)
CGRect CTLineGetImageBounds( CTLineRef line, CGContextRef __nullable context ) CT_AVAILABLE(10_5, 3_2);
从绘制路径CGMutablePathRef开始
void CGPathAddRect(CGMutablePathRef path, const CGAffineTransform *m, CGRect rect);
//*m是一个指向仿射变换矩阵的指针,如果不需要可以设置为NULL,如果指定了矩阵,Core Graphics将转换应用于矩形,然后将其添加到路径中。
//rect就是需要将该矩阵添加到哪里。
/**
* 根据传入的宽高来获取CTFrameRef
*/
- (CTFrameRef)prepareFrameRefWithRect:(CGRect)rect
framesetterRef:(CTFramesetterRef)framesetterRef
{
//CGMutablePathRef是一个可变的路径,可通过CGPathCreateMutable()创建,该路径在被创建后,需要加到Context中。
//CGPathAddRect就是将上面的路径添加到上下文中的方法。
// 创建路径
CGMutablePathRef path = CGPathCreateMutable();
// 添加路径
CGPathAddRect(path, NULL, rect);
// 获取frameRef
// CFRangeMake(0,0) 表示绘制全部文字
CTFrameRef frameRef = CTFramesetterCreateFrame(framesetterRef, CFRangeMake(0, 0), path, NULL);
// 释放内存
CFRelease(path);
return frameRef;
}
网友评论