IOS CoreText.framework --- 行 CTLineRef
http://blog.csdn.net/fengsh998/article/details/8701738
前面两篇文章介绍了文字的样式,段落样式。本文章主要介绍行模式。CTLineRef
知识了解:
1.字符(Character)和字形(Glyphs)
排版系统中文本显示的一个重要的过程就是字符到字形的转换,字符是信息本身的元素,而字形是字符的图形表征,字符还会有其它表征比如发音。 字符在计算机中其实就是一个编码,某个字符集中的编码,比如Unicode字符集,就囊括了大都数存在的字符。 而字形则是图形,一般都存储在字体文件中,字形也有它的编码,也就是它在字体中的索引。 一个字符可以对应多个字形(不同的字体,或者同种字体的不同样式:粗体斜体等);多个字符也可能对应一个字形,比如字符的连写( Ligatures)。
Roman Ligatures
下面就来详情看看字形的各个参数也就是所谓的字形度量Glyph Metrics
- bounding box(边界框 bbox),这是一个假想的框子,它尽可能紧密的装入字形。
- baseline(基线),一条假想的线,一行上的字形都以此线作为上下位置的参考,在这条线的左侧存在一个点叫做基线的原点,
- ascent(上行高度)从原点到字体中最高(这里的高深都是以基线为参照线的)的字形的顶部的距离,ascent是一个正值
- descent(下行高度)从原点到字体中最深的字形底部的距离,descent是一个负值(比如一个字体原点到最深的字形的底部的距离为2,那么descent就为-2)
- linegap(行距),linegap也可以称作leading(其实准确点讲应该叫做External leading),行高lineHeight则可以通过 ascent + |descent| + linegap 来计算。
一些Metrics专业知识还可以参考Free Type的文档 Glyph metrics,其实iOS就是使用Free Type库来进行字体渲染的。
以上图片和部分概念来自苹果文档 Querying Font Metrics ,Text Layout
2.坐标系
首先不得不说 苹果编程中的坐标系花样百出,经常让开发者措手不及。 传统的Mac中的坐标系的原点在左下角,比如NSView默认的坐标系,原点就在左下角。但Mac中有些View为了其实现的便捷将原点变换到左上角,像NSTableView的坐标系坐标原点就在左上角。iOS UIKit的UIView的坐标系原点在左上角。
往底层看,Core Graphics的context使用的坐标系的原点是在左下角。而在iOS中的底层界面绘制就是通过Core Graphics进行的,那么坐标系列是如何变换的呢? 在UIView的drawRect方法中我们可以通过UIGraphicsGetCurrentContext()来获得当前的Graphics Context。drawRect方法在被调用前,这个Graphics Context被创建和配置好,你只管使用便是。如果你细心,通过CGContextGetCTM(CGContextRef c)可以看到其返回的值并不是CGAffineTransformIdentity,通过打印出来看到值为
Printing description of contextCTM:
(CGAffineTransform) contextCTM = {
a = 1
b = 0
c = 0
d = -1
tx = 0
ty = 460
}
这是非retina分辨率下的结果,如果是如果是retina上面的a,d,ty的值将会乘2,如果是iPhone 5,ty的值会再大些。 但是作用都是一样的就是将上下文空间坐标系进行了flip,使得原本左下角原点变到左上角,y轴正方向也变换成向下。
还是老样子,拿一个事先定义好的属性字串进行开讲。
- NSString *src = [NSString stringWithString:@"其实流程是这样的: 1、生成要绘制的NSAttributedString对象。 "];
- NSMutableAttributedString * mabstring = [[NSMutableAttributedString alloc]initWithString:src];
- long slen = [mabstring length];
将属性字串放到frame当中。
- CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)mabstring);
- CGMutablePathRef Path = CGPathCreateMutable();
- //坐标点在左下角
- CGPathAddRect(Path, NULL ,CGRectMake(10 , 10 ,self.bounds.size.width-20 , self.bounds.size.height-20));
- CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL);
显示效果:
得到属性字串在frame中被自动分成了多少个行。每行中有多少个CTRun
- //得到frame中的行数组
- CFArrayRef rows = CTFrameGetLines(frame);
- int rowcount = CFArrayGetCount(rows);
- NSLog(@"rowcount = %i",rowcount);
- CTLineRef line = CFArrayGetValueAtIndex(rows, 0);
- //从一行中得到CTRun数组
- CFArrayRef runs = CTLineGetGlyphRuns(line);
- int runcount = CFArrayGetCount(runs);
- NSLog(@"runcount = %i",runcount);
结果:
- 2013-03-20 23:07:38.835 CTextDemo[5612:207] rowcount = 2
- 2013-03-20 23:07:38.838 CTextDemo[5612:207] runcount = 17
将第一行设置为使用省略号模式
- NSAttributedString *truncatedString = [[NSAttributedString alloc]initWithString:@"\u2026"];
- CTLineRef token = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)truncatedString);
- CTLineTruncationType ltt = kCTLineTruncationStart;//kCTLineTruncationEnd;
- CTLineRef newline = CTLineCreateTruncatedLine(line, self.bounds.size.width-200, ltt, token);
CTLineDraw(newline, context);
效果:
CTLineTruncationType 为kCTLineTrunceationEnd;
省略号在中间
CFIndex CTLineGetGlyphCount( CTLineRef line );
获取一行中的图像个数,即有多少个CTRun。
CFArrayRef CTLineGetGlyphRuns( CTLineRef line );
获取CTRUN数组,可以通过CFArrayGetCount得到数组的个数得到的值与CTLineGetGlyphCount相同。
CGFloat CTLineGetOffsetForStringIndex( CTLineRef line, CFIndex charIndex, CGFloat* secondaryOffset );
获取一行文字中,指定charIndex字符相对x原点的偏移量,返回值与secondaryOffset同为一个值。如果charIndex超出一行的字符长度则反回最大长度结束位置的偏移量,如一行文字共有17个字符,哪么返回的是第18个字符的起始偏移,即第17个偏移+第17个字符占有的宽度=第18个起始位置的偏移。因此想求一行字符所占的像素长度时,就可以使用此函数,将charIndex设置为大于字符长度即可。
- //获取整段文字中charIndex位置的字符相对line的原点的x值
- CGFloat offset;
- CGFloat retoffset = CTLineGetOffsetForStringIndex(line,1,&offset);
- NSLog(@"return offset = %f",retoffset);
- NSLog(@"output offset = %f",offset);
效果:
- 2013-03-21 13:37:22.330 CTextDemo[6851:207] return offset = 12.000000
- 2013-03-21 13:37:22.331 CTextDemo[6851:207] output offset = 12.000000
double CTLineGetPenOffsetForFlush( CTLineRef line, CGFloat flushFactor, double flushWidth );
获取相对于Flush的偏移量。即[flushwidth - line(字符占的像素)]*flushFactor/100;这是我个人推的公式,发现精确度上还存在偏差。
当flushFactor取值为0,0.5,1时分别显示的效果为左对齐,居中对齐,右对齐。
演示代码:
- - (void)drawRect:(CGRect)rect
- {
- NSString *src = [NSString stringWithString:@"其实流程是这样的: 1、生成要绘制的NSAttributedString对象。 "];
- NSMutableAttributedString * mabstring = [[NSMutableAttributedString alloc]initWithString:src];
- long slen = [mabstring length];
- CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)mabstring);
- CGMutablePathRef Path = CGPathCreateMutable();
- //坐标点在左下角
- CGPathAddRect(Path, NULL ,CGRectMake(10 , 10 ,self.bounds.size.width-20 , self.bounds.size.height-20));
- CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL);
- //得到frame中的行数组
- CFArrayRef rows = CTFrameGetLines(frame);
- if (rows) {
- const CFIndex numberOfLines = CFArrayGetCount(rows);
- const CGFloat fontLineHeight = [UIFont systemFontOfSize:20].lineHeight;
- CGFloat textOffset = 0;
- CGContextRef ctx = UIGraphicsGetCurrentContext();
- CGContextSaveGState(ctx);
- CGContextTranslateCTM(ctx, rect.origin.x, rect.origin.y+[UIFont systemFontOfSize:20].ascender);
- CGContextSetTextMatrix(ctx, CGAffineTransformMakeScale(1,-1));
- for (CFIndex lineNumber=0; lineNumber<numberOfLines; lineNumber++) {
- CTLineRef line = CFArrayGetValueAtIndex(rows, lineNumber);
- float flush;
- switch (2) {
- case UITextAlignmentCenter: flush = 0.5; break; //1
- case UITextAlignmentRight: flush = 1; break; //2
- case UITextAlignmentLeft: //0
- default: flush = 0; break;
- }
- CGFloat penOffset = CTLineGetPenOffsetForFlush(line, flush, rect.size.width);
- NSLog(@"penOffset = %f",penOffset);
- CGContextSetTextPosition(ctx, penOffset, textOffset);//在偏移量x,y上打印
- CTLineDraw(line, ctx);//draw 行文字
- textOffset += fontLineHeight;
- }
- CGContextRestoreGState(ctx);
- }
- }
效果:
CFIndex CTLineGetStringIndexForPosition( CTLineRef line, CGPoint position );
获取一行中光标点击处(position)的字符索引,这个值只能为0或最大字符长度。
CFRange CTLineGetStringRange( CTLineRef line );
获取一行字符占的范围(包括换行符一起计算),返回一行位置的起始位置(location)和长度(length)。
location不是每行都从0开始的,而是该行的前N行字符和。
double CTLineGetTrailingWhitespaceWidth( CTLineRef line );
获取一行未尾字符后空格的像素长度。如果:"abc "后面有两个空格,返回的就是这两个空格占有的像素长度。
- double wspace = CTLineGetTrailingWhitespaceWidth(line);
- NSLog(@"whitespacewidth = %f",wspace);
double CTLineGetTypographicBounds( CTLineRef line, CGFloat* ascent, CGFloat* descent, CGFloat* leading );
获取一行中上行高(ascent),下行高(descent),行距(leading),整行高为(ascent+|descent|+leading) 返回值为整行字符串长度占有的像素宽度。
- CGFloat asc,des,lead;
- double lineHeight = CTLineGetTypographicBounds(line, &asc, &des, &lead);
- NSLog(@"ascent = %f,descent = %f,leading = %f,lineheight = %f",asc,des,lead,lineHeight);
CGRect CTLineGetImageBounds( CTLineRef line, CGContextRef context );
获取一行文字的范围,什么意思,就是指把这一行文字点有的像素距阵作为一个image图片,来得到整个矩形区域。
演示代码:
- -(void)drawBounds
- {
- NSString *src = [NSString stringWithString:@"其实流程是这样的: 1、生成要绘制的NSAttributedString对象。 "];
- NSAttributedString * string = [[NSAttributedString alloc]initWithString:src];
- CGContextRef ctx = UIGraphicsGetCurrentContext();
- CGContextSetTextMatrix(ctx , CGAffineTransformIdentity);
- //CGContextSaveGState(ctx);
- //x,y轴方向移动
- CGContextTranslateCTM(ctx , 0 ,self.bounds.size.height);
- //缩放x,y轴方向缩放,-1.0为反向1.0倍,坐标系转换,沿x轴翻转180度
- CGContextScaleCTM(ctx, 1.0 ,-1.0);
- // layout master
- CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(
- (CFAttributedStringRef)string);
- CGMutablePathRef Path = CGPathCreateMutable();
- //坐标点在左下角
- CGPathAddRect(Path, NULL ,CGRectMake(0 , 0 ,self.bounds.size.width , self.bounds.size.height));
- CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), Path, NULL);
- CFArrayRef Lines = CTFrameGetLines(frame);
- int linecount = CFArrayGetCount(Lines);
- CGPoint origins[linecount];
- CTFrameGetLineOrigins(frame,
- CFRangeMake(0, 0), origins);
- NSInteger lineIndex = 0;
- for (id oneLine in (NSArray *)Lines)
- {
- CGRect lineBounds = CTLineGetImageBounds((CTLineRef)oneLine, ctx);
- lineBounds.origin.x += origins[lineIndex].x;
- lineBounds.origin.y += origins[lineIndex].y;
- lineIndex++;
- //画长方形
- //设置颜色,仅填充4条边
- CGContextSetStrokeColorWithColor(ctx, [[UIColor redColor] CGColor]);
- //设置线宽为1
- CGContextSetLineWidth(ctx, 1.0);
- //设置长方形4个顶点
- CGPoint poins[] = {CGPointMake(lineBounds.origin.x, lineBounds.origin.y),CGPointMake(lineBounds.origin.x+lineBounds.size.width, lineBounds.origin.y),CGPointMake(lineBounds.origin.x+lineBounds.size.width, lineBounds.origin.y+lineBounds.size.height),CGPointMake(lineBounds.origin.x, lineBounds.origin.y+lineBounds.size.height)};
- CGContextAddLines(ctx,poins,4);
- CGContextClosePath(ctx);
- CGContextStrokePath(ctx);
- }
- CTFrameDraw(frame,ctx);
- CGPathRelease(Path);
- CFRelease(framesetter);
- }
效果图:
通这个RECT我们可以对文字增加点击事件或其它触发动作等。
OK,CTLine 介绍完毕。
IOS CoreText.framework --- 行 CTLineRef的更多相关文章
- iOS xcode9 framework静态库的创建以及xib和图片的使用记录
来到了新公司,要开发的第一个项目据说可能要封成framework,可是我从来没自己做过framework呀!顿时开始发愤图强,赶紧恶补了起来.但是还是遇到了一些乱七八糟的情况,所以写个随笔记下来. 1 ...
- ios label 自动计算行高详解
在OC当中自动计算行高主要调用系统的 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #ffffff } span ...
- iOS在线更新framework,使用NSBundle动态读取
官方文档:https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/LoadingCode/Tasks/Loadin ...
- iOS Photos.framework框架
链接: iOS8.0 使用Photos.framework对相册的常用操作 iOS AssetsLibrary和Photos的使用总结: 权限及相册的获取 iOS 开发之照片框架详解 iOS Asse ...
- iOS 引入framework的常见问题和原理
今天在引入第三方framework时,我按照以前的方法,把framework加入到了下图的地方: 默认是required的,之后程序就crash了,报错dyld: Library not loaded ...
- iOS 创建framework & bundle 主要配置
bundle:base sdk 为iOS, delete compile resource framework:target dependencies,headers,mach-o proj: tar ...
- iOS 利用 Framework 进行动态更新
http://nixwang.com/2015/11/09/ios-dynamic-update/ 前言 目前 iOS 上的动态更新方案主要有以下 4 种: HTML 5 lua(wax)hotpat ...
- ios统计代码行数
要统计ios开发代码,包括头文件的,终端命令进入项目目录下,命令如下 列出每个文件的行数: find . -name "*.m" -or -name "*.h" ...
- iOS XMPP Framework 中文概述
本篇文章翻译XMPP Framework中的Overview of the XMPP Framework部分 介绍 The framework is divided into 2 parts: 1. ...
随机推荐
- 基于adt-bundle-windows-x86的android开发环境搭建
0,简介: 最近简单着手了解 android 开发.工欲善其事,必先利其器. 我本人不太喜欢使用java 开发,所以简单了解了下其 c# c++都可以进行android 开发,用c++的话要使用NDK ...
- State Machine.(状态机)
What is a State Machine? Any device that changes its state from one to another due to some actions a ...
- Android Gradle 技巧之二: 最爱命令行
命令行 很多做 Android 开发不久的同学,习惯于使用图形界面,对命令行操作很陌生甚至恐惧.遇到 AS 运行错误,束手无策.AS 为了确保易用性,也在 UI 界面上屏蔽了很多命令行运行的细节,导致 ...
- web中session与序列化的问题
最近在写网上商城项目的时候学习了一个关于session的序列化问题,过来总结一下. 众所周知,session是服务器端的一种会话技术,只要session没有关闭,一个会话就会保持.这里先引出一个问题: ...
- RNG vs EDG | SKT vs KTB [20160826]
G1 RNG:丽桑卓,古拉加斯,强行开团流. EDG:崔斯特,普朗克,伊莉斯游走,全球支援流,小规模团战能以多打少. G2 RNG:塔莉垭,纳尔,烬. EDG:雷克塞,艾克,劫,冲击后排. G3 RN ...
- 从Windows 2012标准版升级到数据中心版,标准评价版本升级到标准体验版本并激活
对于Windows 7.Windows 8操作系统,可以在图形界面中通过输入序列号,从低版本直接升级到高的版本,例如从Windows 7家庭版升级到专业版或旗舰版.而对于Windows Server ...
- 在linux下获取帮助
1.使用man手册页 man是一种显示Unix/Linux在线手册的命令.可以用来查看命令.函数或文件的帮助手册,另外它还可以显示一些gzip压缩格式的文件. 读者在遇到不懂的命令时,可以用man查看 ...
- Clustering with the ArcGIS API for Flex
Clustering is an excellent technique for visualizing lotss of point data. We've all seen application ...
- 服务器间打通ssh无密钥
1 打通无密钥 配置HDFS,首先就得把机器之间的无密钥配置上.我们这里为了方便,把机器之间的双向无密钥都配置上. (1)产生RSA密钥信息 ssh-keygen -t rsa 一路回车,直到产生一个 ...
- eclipes(小白)快捷键
Ctrl+shift+l :自动列出全部快捷键 alt+/ :内容助理 Ctrl+1. :快速修复 Ctrl+shift+o ...