iOS_CoreText

ibireme神的源码, CoreText 排版,文字排版的专有名词

CoreText 框架中最常用的几个类:

下面是该框架的结构图

coretext架构图

CTFrame 作为一个整体的画布(Canvas),其中由行(CTLine)组成,

而每行CTLine可以分为一个或多个小方块(CTRun)。


 注意:你不需要自己创建CTRun,Core Text将根据NSAttributedString的属性来自动创建CTRun。

每个CTRun对象对应不同的属性,正因此,你可以自由的控制字体、颜色、字间距等等信息。 


通常处理步聚: 1.使用core text就是先有一个要显示的string,然后定义这个string每个部分的样式->attributedString ->生成 CTFramesetter-> 得到CTFrame -> 绘制(CTFrameDraw) 

其中???可以更详细的设置换行方式,对齐方式,绘制区域的大小等??? 


2.绘制只是显示,点击事件就需要一个判断了。 

CTFrame 包含了多个CTLine,并且可以得到各个line的起始位置与大小。判断点击处在不在某个line上。

CTLine 又可以判断这个点(相对于CTLine的坐标)处的文字范围。然后遍历这个string的所有NSTextCheckingResult根据result的rang判断点击处在不在这个rang上,从而得到点击的链接与位置。

字体的基本知识

字体(Font):是一系列字号、样式和磅值相同的字符(例如:10磅黑体Palatino)。现多被视为字样的同义词

字面(Face):所有字号的磅值和格式的综合???

字体集(Font family):是一组相关字体(例如:Franklin family包括Franklin Gothic、Fran-klinHeavy和Franklin Compressed)

磅值(Weight):用于描述字体粗度。典型的磅值,从最粗到最细,有极细、细、book、中等、半粗、粗、较粗、极粗

样式(Style):字形有三种形式:

Roman type是直体;

obliquetype是斜体;

utakuc type是斜体兼曲线(比Roman type更像书法体)???


x高度(X height):指小写字母的平均高度(以x为基准)。磅值相同的两字母,x高度越大的字母看起来比x高度小的字母要大

Cap高度(Cap height):与x高度相似。指大写字母的平均高度(以C为基准)

下行字母(Descender):例如在字母q中,基线以下的字母部分叫下伸部分

上行字母(Ascender):x高度以上的部分(比如字母b)叫做上伸部分

基线(Baseline):通常在x、v、b、m下的那条线 描边(Stroke):组成字符的线或曲线。可以加粗或改变字符形状

衬线(Serif):用来使字符更可视的一条水平线。如字母左上角和下部的水平线。

无衬线(Sans Serif):可以让排字员不使用衬线装饰。

方形字(Block):这种字体的笔画使字符看起来比无衬线字更显眼,但还不到常见的衬线字的程度。例如Lubalin Graph就是方形字,这种字看起来好像是木头块刻的一样

手写体脚本(Calligraphic script):是一种仿效手写体的字体。例如Murray Hill或者Fraktur字体

艺术字(Decorative):像绘画般的字体

Pi符号(Pisymbol):非标准的字母数字字符的特殊符号。例如Wingdings和Mathematical Pi

连写(Ligature):是一系列连写字母如fi、fl、ffi或ffl。由于字些字母形状的原因经常被连写,故排字员已习惯将它们连写。

读完了上面这些概念,可以参考一下下面的图片,看看具体的位置

文字结构 


文字结构





其中,在 Apple 的 SDK 中是这样定义这些属性的



const CFStringRef kCTCharacterShapeAttributeName;
//字体形状属性  必须是CFNumberRef对象默认为0,非0则对应相应的字符形状定义,如1表示传统字符形状


const CFStringRef kCTFontAttributeName;
//字体属性   必须是CTFont对象


const CFStringRef kCTKernAttributeName;
//字符间隔属性 必须是CFNumberRef对象


const CFStringRef kCTLigatureAttributeName;
//设置是否使用连字属性,设置为0,表示不使用连字属性。标准的英文连字有FI,FL.默认值为1,既是使用标准连字。也就是当搜索到f时候,会把fl当成一个文字。必须是CFNumberRef 默认为1,可取0,1,2


const CFStringRef kCTForegroundColorAttributeName;
//字体颜色属性  必须是CGColor对象,默认为black


const CFStringRef kCTForegroundColorFromContextAttributeName;
 //上下文的字体颜色属性 必须为CFBooleanRef 默认为False


const CFStringRef kCTParagraphStyleAttributeName;
//段落样式属性 必须是CTParagraphStyle对象 默认为NIL


const CFStringRef kCTStrokeWidthAttributeName;
//笔画线条宽度 必须是CFNumberRef对象,默为0.0f,标准为3.0f
const CFStringRef kCTStrokeColorAttributeName;
//笔画的颜色属性 必须是CGColorRef 对象,默认为前景色


const CFStringRef kCTSuperscriptAttributeName;
//设置字体的上下标属性 必须是CFNumberRef对象 默认为0,可为-1为下标,1为上标,需要字体支持才行。如排列组合的样式Cn1


const CFStringRef kCTUnderlineColorAttributeName;
//字体下划线颜色属性 必须是CGColorRef对象,默认为前景色


const CFStringRef kCTUnderlineStyleAttributeName;
//字体下划线样式属性 必须是CFNumberRef对象,默为kCTUnderlineStyleNone 可以通过CTUnderlineStyleModifiers 进行修改下划线风格


const CFStringRef kCTVerticalFormsAttributeName;
//文字的字形方向属性 必须是CFBooleanRef 默认为false,false表示水平方向,true表示竖直方向


const CFStringRef kCTGlyphInfoAttributeName;
//字体信息属性 必须是CTGlyphInfo对象


const CFStringRef kCTRunDelegateAttributeName
//CTRun 委托属性 必须是CTRunDelegate对象


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const CFStringRef kCTCharacterShapeAttributeName;
//字体形状属性  必须是CFNumberRef对象默认为0,非0则对应相应的字符形状定义,如1表示传统字符形状

const CFStringRef kCTFontAttributeName;
//字体属性   必须是CTFont对象

const CFStringRef kCTKernAttributeName;
//字符间隔属性 必须是CFNumberRef对象

const CFStringRef kCTLigatureAttributeName;
//设置是否使用连字属性,设置为0,表示不使用连字属性。标准的英文连字有FI,FL.默认值为1,既是使用标准连字。也就是当搜索到f时候,会把fl当成一个文字。必须是CFNumberRef 默认为1,可取0,1,2

const CFStringRef kCTForegroundColorAttributeName;
//字体颜色属性  必须是CGColor对象,默认为black

const CFStringRef kCTForegroundColorFromContextAttributeName;
 //上下文的字体颜色属性 必须为CFBooleanRef 默认为False

const CFStringRef kCTParagraphStyleAttributeName;
//段落样式属性 必须是CTParagraphStyle对象 默认为NIL

const CFStringRef kCTStrokeWidthAttributeName;
//笔画线条宽度 必须是CFNumberRef对象,默为0.0f,标准为3.0f
const CFStringRef kCTStrokeColorAttributeName;
//笔画的颜色属性 必须是CGColorRef 对象,默认为前景色

const CFStringRef kCTSuperscriptAttributeName;
//设置字体的上下标属性 必须是CFNumberRef对象 默认为0,可为-1为下标,1为上标,需要字体支持才行。如排列组合的样式Cn1

const CFStringRef kCTUnderlineColorAttributeName;
//字体下划线颜色属性 必须是CGColorRef对象,默认为前景色

const CFStringRef kCTUnderlineStyleAttributeName;
//字体下划线样式属性 必须是CFNumberRef对象,默为kCTUnderlineStyleNone 可以通过CTUnderlineStypleModifiers 进行修改下划线风格

const CFStringRef kCTVerticalFormsAttributeName;
//文字的字形方向属性 必须是CFBooleanRef 默认为false,false表示水平方向,true表示竖直方向

const CFStringRef kCTGlyphInfoAttributeName;
//字体信息属性 必须是CTGlyphInfo对象

const CFStringRef kCTRunDelegateAttributeName
//CTRun 委托属性 必须是CTRunDelegate对象

例如:

1
NSMutableAttributedString *mabstring = [[NSMutableAttributedString alloc]initWithString:@"This is a test of characterAttribute. 中文字符"];

文字结构

1
2
3
  //设置字体属性
    CTFontRef font = CTFontCreateWithName(CFSTR("Georgia"), 40, NULL);
    [mabstring addAttribute:(id)kCTFontAttributeName value:(id)font range:NSMakeRange(0, 4)];

文字结构

1
2
3
//设置斜体字
    CTFontRef font = CTFontCreateWithName((CFStringRef)[UIFont italicSystemFontOfSize:20].fontName, 14, NULL);
    [mabstring addAttribute:(id)kCTFontAttributeName value:(id)font range:NSMakeRange(0, 4)];

文字结构


kCTUnderlineStyleAttributeName
kCTUnderlineStyleDouble

1
2
//下划线
    [mabstring addAttribute:(id)kCTUnderlineStyleAttributeName value:(id)[NSNumber numberWithInt:kCTUnderlineStyleDouble] range:NSMakeRange(0, 4)];

文字结构

1
2
//下划线颜色
    [mabstring addAttribute:(id)kCTUnderlineColorAttributeName value:(id)[UIColor redColor].CGColor range:NSMakeRange(0, 4)];

文字结构

1
2
3
4
//设置字体简隔 eg:test 
    long number = 10;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTKernAttributeName value:(id)num range:NSMakeRange(10, 4)];

文字结构

1
2
3
4
//设置连字
long number = 1;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTLigatureAttributeName value:(id)num range:NSMakeRange(0, [str length])];

暂时没有效果图

1
2
//设置字体颜色
    [mabstring addAttribute:(id)kCTForegroundColorAttributeName value:(id)[UIColor redColor].CGColor range:NSMakeRange(0, 9)];

文字结构

1
2
3
//设置字体颜色为前影色
    CFBooleanRef flag = kCFBooleanTrue;    [mabstring addAttribute:(id)kCTForegroundColorFromContextAttributeName value:(id)flag range:NSMakeRange(5, 10)];

暂无效果。。


1
2
3
4
//设置空心字
    long number = 2;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTStrokeWidthAttributeName value:(id)num range:NSMakeRange(0, [str length])];

文字结构

1
2
3
4
5
6
7
//设置空心字
    long number = 2;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTStrokeWidthAttributeName value:(id)num range:NSMakeRange(0, [str length])];

    //设置空心字颜色
    [mabstring addAttribute:(id)kCTStrokeColorAttributeName value:(id)[UIColor greenColor].CGColor range:NSMakeRange(0, [str length])];

文字结构

在设置空心字颜色时,必须先将字体高为空心,否则设置颜色是没有效果的。

1
2
3
4
5
6
7
8
9
10
11
//对同一段字体进行多属性设置    
    //红色
    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(id)[UIColor redColor].CGColor forKey:(id)kCTForegroundColorAttributeName];
    //斜体
    CTFontRef font = CTFontCreateWithName((CFStringRef)[UIFont italicSystemFontOfSize:20].fontName, 40, NULL);
    [attributes setObject:(id)font forKey:(id)kCTFontAttributeName];
    //下划线
    [attributes setObject:(id)[NSNumber numberWithInt:kCTUnderlineStyleDouble] forKey:(id)kCTUnderlineStyleAttributeName];

    [mabstring addAttributes:attributes range:NSMakeRange(0, 4)];

文字结构

最后 Draw 一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
-(void)characterAttribute
{
    NSString *str = @"This is a test of characterAttribute. 中文字符";
    NSMutableAttributedString *mabstring = [[NSMutableAttributedString alloc]initWithString:str];

    [mabstring beginEditing];
    /*
    long number = 1;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTCharacterShapeAttributeName value:(id)num range:NSMakeRange(0, 4)];
    */
    /*
    //设置字体属性
    CTFontRef font = CTFontCreateWithName(CFSTR("Georgia"), 40, NULL);
    [mabstring addAttribute:(id)kCTFontAttributeName value:(id)font range:NSMakeRange(0, 4)];
    */
    /*
    //设置字体简隔 eg:test 
    long number = 10;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTKernAttributeName value:(id)num range:NSMakeRange(10, 4)];
    */

    /*
    long number = 1;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTLigatureAttributeName value:(id)num range:NSMakeRange(0, [str length])];
     */
    /*
    //设置字体颜色
    [mabstring addAttribute:(id)kCTForegroundColorAttributeName value:(id)[UIColor redColor].CGColor range:NSMakeRange(0, 9)];
     */
    /*
    //设置字体颜色为前影色
    CFBooleanRef flag = kCFBooleanTrue;
    [mabstring addAttribute:(id)kCTForegroundColorFromContextAttributeName value:(id)flag range:NSMakeRange(5, 10)];
     */

    /*
    //设置空心字
    long number = 2;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTStrokeWidthAttributeName value:(id)num range:NSMakeRange(0, [str length])];
     
    //设置空心字颜色
    [mabstring addAttribute:(id)kCTStrokeColorAttributeName value:(id)[UIColor greenColor].CGColor range:NSMakeRange(0, [str length])];
     */

    /*
    long number = 1;
    CFNumberRef num = CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt8Type,&number);
    [mabstring addAttribute:(id)kCTSuperscriptAttributeName value:(id)num range:NSMakeRange(3, 1)];
    */

    /*
    //设置斜体字
    CTFontRef font = CTFontCreateWithName((CFStringRef)[UIFont italicSystemFontOfSize:20].fontName, 14, NULL);
    [mabstring addAttribute:(id)kCTFontAttributeName value:(id)font range:NSMakeRange(0, 4)];
    */

    /*
    //下划线
    [mabstring addAttribute:(id)kCTUnderlineStyleAttributeName value:(id)[NSNumber numberWithInt:kCTUnderlineStyleDouble] range:NSMakeRange(0, 4)]; 
    //下划线颜色
    [mabstring addAttribute:(id)kCTUnderlineColorAttributeName value:(id)[UIColor redColor].CGColor range:NSMakeRange(0, 4)];
     */



    //对同一段字体进行多属性设置    
    //红色
    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(id)[UIColor redColor].CGColor forKey:(id)kCTForegroundColorAttributeName];
    //斜体
    CTFontRef font = CTFontCreateWithName((CFStringRef)[UIFont italicSystemFontOfSize:20].fontName, 40, NULL);
    [attributes setObject:(id)font forKey:(id)kCTFontAttributeName];
    //下划线
    [attributes setObject:(id)[NSNumber numberWithInt:kCTUnderlineStyleDouble] forKey:(id)kCTUnderlineStyleAttributeName];

    [mabstring addAttributes:attributes range:NSMakeRange(0, 4)];



    NSRange kk = NSMakeRange(0, 4);

    NSDictionary * dc = [mabstring attributesAtIndex:0 effectiveRange:&kk];

    [mabstring endEditing];

    NSLog(@"value = %@",dc);



    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)mabstring);

    CGMutablePathRef Path = CGPathCreateMutable();

    CGPathAddRect(Path, NULL ,CGRectMake(10 , 0 ,self.bounds.size.width-10 , self.bounds.size.height-10));

    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL);    

    //获取当前(View)上下文以便于之后的绘画,这个是一个离屏。
    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetTextMatrix(context , CGAffineTransformIdentity);

    //压栈,压入图形状态栈中.每个图形上下文维护一个图形状态栈,并不是所有的当前绘画环境的图形状态的元素都被保存。图形状态中不考虑当前路径,所以不保存
    //保存现在得上下文图形状态。不管后续对context上绘制什么都不会影响真正得屏幕。
    CGContextSaveGState(context);

    //x,y轴方向移动
    CGContextTranslateCTM(context , 0 ,self.bounds.size.height);

    //缩放x,y轴方向缩放,-1.0为反向1.0倍,坐标系转换,沿x轴翻转180度
    CGContextScaleCTM(context, 1.0 ,-1.0);

    CTFrameDraw(frame,context);

    CGPathRelease(Path);
    CFRelease(framesetter);
}


- (void)drawRect:(CGRect)rect
{
    [self characterAttribute];
}



前言

这个是前言

引言

学习CoreText,写一个杂志类的应用,如网易和zarca应用

难题

1、对CGContextRef的CTM理解。
2、对NS的了解,文字绘制方面的座标系。
3、对CoreText的API。

关于CTM

CTM,Context Translate Matrix。 它是把要绘制的上下文以一个叫做Matrix的东西来表示,可以简单地想作,绘制的上下文的每一个点都映射在Matrix上,你在Matrix上的操作都会使得上下文上的点产生相应的变动。如放大、旋转、移动。

为了达到旋转或放大缩小的目的,一般都会先改变这个上下文,如:

1
2
3
4
5
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0f, -1.0f);

// some draw code
// ....

然后进行绘图操作。那么这个绘图操作是怎么做的呢?这个对Matrix的操作,为什么是放在前面而不是放在后面,先从context来了解。

一般情况也,我们误认为context就是画布,所有的matrix旋转都是针对画布的旋转,但是这样的理解是错误的,虽然得到的结果是正确的

其实context说的是绘画人所处的角度上下文。如下图,默认的情况下,绘画人的角度是正对着画布的:

画布是白色的,在左上角用一个黄色的三角形来标识它的左上角,使用left top来标识context的左上角,而绘画人是黄色的圆形。

要记着!!画布无论怎么样都是正对着屏幕的,它不会旋转,或者放大缩小,或者移动。

那么为什么又看起来放大了或者移动了呢?其实移动的是你的context,也就是你所处的context视角,我举个例子,比方说我要旋转180度在左上角写一个“abcdefg”。

首先,我要先旋转180度:

然后,我在左上角写上“abcdefg”:

然后重置context:

可以看到,我们改变context只是改变了自己面对画布的角度,而画布仍然是正对着屏幕的,刚才绘画abcdefg时的座标(0,0)是你旋转后context的left top,而不是画布 的左上角,记着这一点很重要。(因为旋转了context180度,实际上座标0,0是画布的右下角了,始终记住画布它从来就没动过,画布始终是正对着屏幕的

所以,在绘画的时候,其实是倒着画在了画布的右下角上。而重置context,则是把自己正对着画布而已。这也就说清了为什么是在使用matrix更改context之后进行绘图有效(把自己面对画布的角度先调整了),而不是在画了之后再调整(因为你都画完了,再调整自己的角度还有什么用?)。

正确理解使用matrix更改context的方式很重要,因为这涉及到坐标系的问题,之后的CoreText相当讨论会讲到一个例子。

关于NS座标系

NS坐标系是以左下角为(0,0),所以NS坐标系是与iOS的UIKit坐标系在Y上是相反的,所以,在iOS进行CoreText进行绘图或文字的时候,X方向是一致的,但是Y则是倒过来的。当你在(20, 20)的坐标处用CoreText画一个长方形,其实就是画布的(20, canvas.height - 20 + rect.size.height)的位置上画了个长方形,而且是倒过来的。如下图:

那么怎么办呢?想想,仔细看上面这张图,貌似像是正常方向的倒影,但是水平线却在最上面。嗯,挪下来,然后再反过来,看一下效果。如下图:

效果:

效果果然如图所示,好!!

可是是不是就这样完了呢?不是,还有一个更为重要的问题,这个时候,进行了两次的转换matrix,context的left top在哪里呢?

根据之前的理论,那得让自己先把自己向下移,然后把头倒过来,OK,这下明白了,这下画布的左下角变成了context的左上角,别的都没变。这时,当你在(20, 20)的坐标处用CoreText画一个长方形,其实就是画布的(20, canvas.height - 20 + rect.size.height)的位置上画了个长方形,而且是倒过来的。

仔细想想这个,有趣的事情还有很多,因为按照自己看过本文之前的理论,可能会非常惊讶为什么得到的结果和自己想的不一样,一直以为是在(20, 20)处画一个长方形,结果却刚好相反,这就是没有理解context及matrix的正确含义所致。

关于CoreText的API

使用Create函数建立的对象引用,必须要使用CFRelease掉。

CoreText是什么?

这是一个低级的API,它的数据源是NSAttributedString。它可以根据NSAttributedString的定义的每个range的subNSAttributedString的样式进行对字符串的渲染。可以这样说,这是一个富文本渲染器。

为什么要用CoreText

UIWebView做不了一些东西,比如说分列显示,这点webpage难以做到

那么为什么要用CoreText呢?其实很重要的一点是那个无用的UILabel控件,尤其是多行文本时,它竟然只能垂直居中。

另,使用CoreText可以很好地做一些个性化的东西,比如可以使用动画,这一点UIWebView做不到。它能做一些很cool的东西,比方说,杂志,新闻类的应用。

CoreText的概念。

CTFramesetterRef
按名字就知道,这是一个setter,一个属性设置器,它引用了一些有用的对像,诸如字体之类的,它的工作就是,生成一个CTFrameRef对象

CTFrameRef
这是一个Frame对象,用于表示一个绘制区域,它是由CTFramesetterRef生成。

CTLineRef
表示要绘制的一行。CTFrameRef对象包含了多个line对象。

CTNodeRef
表示要绘制的某个节点(subNSAttributedString),每line对象包含多个node对象,这个节点表示着不同格式的NSAttributedString对象的如何绘制。在中间插入图片的话,这个就要考虑了。CoreText是不支持中间插入图片的,不过我们可以在读到特殊标记的node的时候,返回不同的行高和行宽,预留空间,在绘制完coretext之后,在这些个空间处绘制相应的图片。

CoreText怎么使用呢?

其实流程是这样的: 

1、生成要绘制的NSAttributedString对象。

 2、生成一个CTFramesetterRef对象,然后创建一个CGPath对象,这个Path对象用于表示可绘制区域坐标值、长宽。

 3、使用上面生成的setter和path生成一个CTFrameRef对象,这个对象包含了这两个对象的信息(字体信息、坐标信息),它就可以使用CTFrameDraw方法绘制了。

这里有一个demo代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)_drawRectWithContext:(CGContextRef)context
{
  // generate NSAttributedString object
    NSAttributedString *contentAttrString = [self _generateAttributeString];

  // 初始一个 path 
    CGRect bounds = CGRectInset(self.bounds, 10.0f, 10.0f);
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, bounds);

    // ------------------------ begin draw
    // 初始一个framesetter
    CTFramesetterRef framesetter
        = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(contentAttrString));

	// 通过 framesetter 和 path 生成 Frame
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
    CTFrameDraw(frame, context);

    CFRelease(frame);
    CFRelease(path);
    CFRelease(framesetter);
    // ------------------------ end draw
}

NSAttributedString呢?

一直都在说CoreText的事情,那么怎么使用NSAttributedString呢?怎么设置NSAttributedString的属性,怎么设计行高,怎么设置字体大小,样式?

首先得会设置属性,调用NSAttributedString的setAttributes:range:方法就可以设置属性:

1
2
3
// set attributes to attributed string
[attrString setAttributes:attributes
                    range:NSMakeRange(0, attrString.length)];

那么都有哪些属性呢?有下面这些属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const CFStringRef kCTCharacterShapeAttributeName;
const CFStringRef kCTFontAttributeName;
const CFStringRef kCTKernAttributeName;
const CFStringRef kCTLigatureAttributeName;
const CFStringRef kCTForegroundColorAttributeName;
const CFStringRef kCTForegroundColorFromContextAttributeName;
const CFStringRef kCTParagraphStyleAttributeName;
const CFStringRef kCTStrokeWidthAttributeName;
const CFStringRef kCTStrokeColorAttributeName;
const CFStringRef kCTSuperscriptAttributeName;
const CFStringRef kCTUnderlineColorAttributeName;
const CFStringRef kCTUnderlineStyleAttributeName;
const CFStringRef kCTVerticalFormsAttributeName;
const CFStringRef kCTGlyphInfoAttributeName;
const CFStringRef kCTRunDelegateAttributeName

下面是说明:

kCTCharacterShapeAttributeName
Controls glyph selection. Value must be a CFNumberRef object. Default is value is 0 (disabled). A non-zero value is interpreted as Apple Type Services kCharacterShapeType selector + 1 (see <ATS/SFNTLayoutTypes.h> for selectors). For example, an attribute value of 1 corresponds to kTraditionalCharactersSelector.

kCTFontAttributeName
The font of the text to which this attribute applies. The value associated with this attribute must be a CTFont object. Default is Helvetica 12.

kCTKernAttributeName
The amount to kern the next character. The value associated with this attribute must be a CFNumber float. Default is standard kerning. The kerning attribute indicates how many points the following character should be shifted from its default offset as defined by the current character’s font in points: a positive kern indicates a shift farther away from and a negative kern indicates a shift closer to the current character. If this attribute is not present, standard kerning is used. If this attribute is set to 0.0, no kerning is done at all.

kCTLigatureAttributeName
The type of ligatures to use. The value associated with this attribute must be a CFNumber object. Default is an integer value of 1. The ligature attribute determines what kinds of ligatures should be used when displaying the string. A value of 0 indicates that only ligatures essential for proper rendering of text should be used. A value of 1 indicates that standard ligatures should be used, and 2 indicates that all available ligatures should be used. Which ligatures are standard depends on the script and possibly the font. Arabic text, for example, requires ligatures for many character sequences but has a rich set of additional ligatures that combine characters. English text has no essential ligatures, and typically has only two standard ligatures, those for “fi” and “fl”—all others are considered more advanced or fancy.

kCTForegroundColorAttributeName
The foreground color of the text to which this attribute applies. The value associated with this attribute must be a CGColor object. Default value is black.

kCTForegroundColorFromContextAttributeName
Sets a foreground color using the context’s fill color. Value must be a CFBooleanRef object. Default is false. The reason this exists is because an NSAttributedString object defaults to a black color if no color attribute is set. This forces Core Text to set the color in the context. This attribute allows developers to sidestep this, making Core Text set nothing but font information in the CGContext. If set, this attribute also determines the color used by kCTUnderlineStyleAttributeName, in which case it overrides the foreground color.

kCTParagraphStyleAttributeName
The paragraph style of the text to which this attribute applies. A paragraph style object is used to specify things like line alignment, tab rulers, writing direction, and so on. Value must be a CTParagraphStyle object. Default is an empty CTParagraphStyle object. See CTParagraphStyle Reference for more information.

kCTStrokeWidthAttributeName
The stroke width. Value must be a CFNumberRef object. Default value is 0.0, or no stroke. This attribute, interpreted as a percentage of font point size, controls the text drawing mode: positive values effect drawing with stroke only; negative values are for stroke and fill. A typical value for outlined text is 3.0.

kCTStrokeColorAttributeName
The stroke color. Value must be a CGColorRef object. Default is the foreground color.

kCTSuperscriptAttributeName
Controls vertical text positioning. Value must be a CFNumberRef object. Default is integer value 0. If supported by the specified font, a value of 1 enables superscripting and a value of -1 enables subscripting.

kCTUnderlineColorAttributeName
The underline color. Value must be a CGColorRef object. Default is the foreground color.

kCTUnderlineStyleAttributeName
The style of underlining, to be applied at render time, for the text to which this attribute applies. Value must be a CFNumber object. Default is kCTUnderlineStyleNone. Set a value of something other than kCTUnderlineStyleNone to draw an underline. In addition, the constants listed in “CTUnderlineStyleModifiers” can be used to modify the look of the underline. The underline color is determined by the text’s foreground color.

kCTVerticalFormsAttributeName
The orientation of the glyphs in the text to which this attribute applies. Value must be a CFBoolean object. Default is False. A value of False indicates that horizontal glyph forms are to be used; True indicates that vertical glyph forms are to be used.

kCTGlyphInfoAttributeName
The glyph info object to apply to the text associated with this attribute. Value must be a CTGlyphInfo object. The glyph specified by this CTGlyphInfo object is assigned to the entire attribute range, provided that its contents match the specified base string and that the specified glyph is available in the font specified by kCTFontAttributeName. See CTGlyphInfo Reference for more information.

kCTRunDelegateAttributeName
The run-delegate object to apply to an attribute range of the string. The value must be a CTRunDelegate object. The run delegate controls such typographic traits as glyph ascent, descent, and width. The values returned by the embedded run delegate apply to each glyph resulting from the text in that range. Because an embedded object is only a display-time modification, you should avoid applying this attribute to a range of text with complex behavior, such as text having a change of writing direction or having combining marks. It is thus recommended you apply this attribute to a range containing the single character U+FFFC. See CTRunDelegate Reference for more information.

上面所述的东西貌似只是说明了设置字体与样式,却没有行高、缩进之类的东西哦!!

嗯,不是没有,而是CoreText把它当成是段落样式来设置了,也就是说,要设置kCTParagraphStyleAttributeName的属性就行。kCTParagraphStyleAttributeName属性的值是一个CTParagraphStyle对象,你需要把你想要设置的段落属性放进这个对象就可以设置行高之类的东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kCTParagraphStyleSpecifierAlignment = 0,
kCTParagraphStyleSpecifierFirstLineHeadIndent = 1,
kCTParagraphStyleSpecifierHeadIndent = 2,
kCTParagraphStyleSpecifierTailIndent = 3,
kCTParagraphStyleSpecifierTabStops = 4,
kCTParagraphStyleSpecifierDefaultTabInterval = 5,
kCTParagraphStyleSpecifierLineBreakMode = 6,
kCTParagraphStyleSpecifierLineHeightMultiple = 7,
kCTParagraphStyleSpecifierMaximumLineHeight = 8,
kCTParagraphStyleSpecifierMinimumLineHeight = 9,
kCTParagraphStyleSpecifierLineSpacing = 10,          /* deprecated */
kCTParagraphStyleSpecifierParagraphSpacing = 11,
kCTParagraphStyleSpecifierParagraphSpacingBefore = 12,
kCTParagraphStyleSpecifierBaseWritingDirection = 13,
kCTParagraphStyleSpecifierMaximumLineSpacing = 14,
kCTParagraphStyleSpecifierMinimumLineSpacing = 15,
kCTParagraphStyleSpecifierLineSpacingAdjustment = 16,
kCTParagraphStyleSpecifierLineBoundsOptions = 17,

kCTParagraphStyleSpecifierCount

下面是说明:

kCTParagraphStyleSpecifierAlignment
The text alignment. Natural text alignment is realized as left or right alignment, depending on the line sweep direction of the first script contained in the paragraph. Type: CTTextAlignment. Default: kCTNaturalTextAlignment. Application: CTFramesetter.

kCTParagraphStyleSpecifierFirstLineHeadIndent
The distance, in points, from the leading margin of a frame to the beginning of the paragraph’s first line. This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierHeadIndent
The distance, in points, from the leading margin of a text container to the beginning of lines other than the first. This value is always nonnegative. Type: CGFloat Default: 0.0 Application: CTFramesetter

kCTParagraphStyleSpecifierTailIndent
The distance, in points, from the margin of a frame to the end of lines. If positive, this value is the distance from the leading margin (for example, the left margin in left-to-right text). If 0 or negative, it’s the distance from the trailing margin. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierTabStops
The CTTextTab objects, sorted by location, that define the tab stops for the paragraph style. Type: CFArray of CTTextTabRef. Default: 12 left-aligned tabs, spaced by 28.0 points. Application: CTFramesetter, CTTypesetter.

kCTParagraphStyleSpecifierDefaultTabInterval
The documentwide default tab interval. Tabs after the last specified by kCTParagraphStyleSpecifierTabStops are placed at integer multiples of this distance (if positive). Type: CGFloat. Default: 0.0. Application: CTFramesetter, CTTypesetter.

kCTParagraphStyleSpecifierLineBreakMode
The mode that should be used to break lines when laying out the paragraph’s text. Type: CTLineBreakMode. Default: kCTLineBreakByWordWrapping. Application: CTFramesetter

kCTParagraphStyleSpecifierLineHeightMultiple
The line height multiple. The natural line height of the receiver is multiplied by this factor (if positive) before being constrained by minimum and maximum line height. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierMaximumLineHeight
The maximum height that any line in the frame will occupy, regardless of the font size or size of any attached graphic. Glyphs and graphics exceeding this height will overlap neighboring lines. A maximum height of 0 implies no line height limit. This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierMinimumLineHeight
The minimum height that any line in the frame will occupy, regardless of the font size or size of any attached graphic. This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierLineSpacing
The space in points added between lines within the paragraph (commonly known as leading). This value is always nonnegative. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierParagraphSpacing
The space added at the end of the paragraph to separate it from the following paragraph. This value is always nonnegative and is determined by adding the previous paragraph’s kCTParagraphStyleSpecifierParagraphSpacing setting and the current paragraph’s kCTParagraphStyleSpecifierParagraphSpacingBefore setting. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierParagraphSpacingBefore
The distance between the paragraph’s top and the beginning of its text content. Type: CGFloat. Default: 0.0. Application: CTFramesetter.

kCTParagraphStyleSpecifierBaseWritingDirection
The base writing direction of the lines. Type: CTWritingDirection. Default: kCTWritingDirectionNatural. Application: CTFramesetter, CTTypesetter.

kCTParagraphStyleSpecifierCount
The number of style specifiers. The purpose is to simplify validation of style specifiers

那么怎么编码呢?也就是这些属性怎么用呢?下面是一个demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:_content];

// line space
CTParagraphStyleSetting lineSpaceSetting;
lineSpaceSetting.spec = kCTParagraphStyleSpecifierLineSpacing;
lineSpaceSetting.value = &_lineSpace;
lineSpaceSetting.valueSize = sizeof(float);

// indent
CTParagraphStyleSetting indentSetting;
indentSetting.spec = kCTParagraphStyleSpecifierFirstLineHeadIndent;
indentSetting.value = &_indent;
indentSetting.valueSize = sizeof(float);

// composite settings
CTParagraphStyleSetting settings[] = {
    lineSpaceSetting,
    indentSetting
};
CTParagraphStyleRef style = CTParagraphStyleCreate(settings, 2);

// build attributes
NSDictionary *attributes = @{(__bridge id)kCTParagraphStyleAttributeName: (__bridge id)style};

// set attributes to attributed string
[attrString setAttributes:attributes
                    range:NSMakeRange(0, attrString.length)];

分栏显示

这个东西是相当有趣的东东,可以使用CTFrameGetVisibleStringRange函数来计算指定frame绘制了多少字符,那么就可以另建一个frame把剩余的字符绘制进去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// get current context and store it's state
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);

    // translate CTM for iOS
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1, -1);

    // generate attributed string
    NSAttributedString *attrString = [self _generateAttributedString];


    // Draw code start -------------------------------------------------------------------------------------------------
    CGRect bounds = CGRectInset(self.bounds, 25.0f, 25.0f);
    float columnWidth = (bounds.size.width - 30.0f) / 2.0f;
    float columnHeight = bounds.size.height;


    CTFramesetterRef framesetter
        = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)(attrString));
    int location = 0;

    // ------------- first column
    CGRect firstColumnRect = CGRectMake(bounds.origin.x, bounds.origin.y, columnWidth, columnHeight);
    CGMutablePathRef firstColumnPath = CGPathCreateMutable();
    CGPathAddRect(firstColumnPath, NULL, firstColumnRect);

    CTFrameRef firstColumnFrame =
        CTFramesetterCreateFrame(framesetter, CFRangeMake(location, 0), firstColumnPath, NULL);
    CFRange firstColumnStringRange = CTFrameGetVisibleStringRange(firstColumnFrame);
    CTFrameDraw(firstColumnFrame, context);

    // recalculate the location for next frame.
    location = firstColumnStringRange.length;


    // ------------- second column
    CGRect secondColumnRect =
    CGRectMake(bounds.origin.x + 30 + columnWidth, bounds.origin.y, columnWidth, columnHeight);
    CGMutablePathRef secondColumnPath = CGPathCreateMutable();
    CGPathAddRect(secondColumnPath, NULL, secondColumnRect);

    CTFrameRef secondColumnFrame =
        CTFramesetterCreateFrame(framesetter,
                                 CFRangeMake(location, 0),
                                 secondColumnPath, NULL);
    CTFrameDraw(secondColumnFrame, context);

    // release
    CFRelease(firstColumnPath);
    CFRelease(firstColumnFrame);
    CFRelease(secondColumnPath);
    CFRelease(secondColumnFrame);
    CFRelease(framesetter);

    // Draw code end   -------------------------------------------------------------------------------------------------

    // restore current context
    CGContextRestoreGState(context);

总结





















  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值