CoreText的绘制流程-转
来自:http://blog.sina.com.cn/s/blog_7c8dc2d50101lbb1.html
使用coreText进行文本绘制,需要在工程中添加CoreText.framework,然后在AttributedLabel.m里import就可以使用了。coreText负责绘制,那绘制的内容和属性则要靠NSAttributedString来存储,如果属性具有不确定性,可以使用NSMutableAttributedString,方便后面添加属性。
先来看下如何创建一个具有两个颜色,两种字体的“hello world”的NSMutableAttributedString实例。
NSString *text = @"hello word"; NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text]; [attributedText addAttribute:(NSString*)(kCTForegroundColorAttributeName) value:(id)[[UIColor blueColor]CGColor] range:NSMakeRange(,)]; [attributedText addAttribute:(NSString*)(kCTForegroundColorAttributeName) value:(id)[[UIColor redColor]CGColor] range:NSMakeRange(,)]; CTFontRef font_hello = CTFontCreateWithName((CFStringRef)@"Helvetica",,NULL); CTFontRef font_world = CTFontCreateWithName((CFStringRef)@"GillSans",,NULL); [attributedText addAttribute: (NSString*)(kCTFontAttributeName) value:(id)font_hello range:NSMakeRange(,)]; [attributedText addAttribute: (NSString*)(kCTFontAttributeName) value:(id)font_world range:NSMakeRange(,)];
这样,一个包含简单绘制属性的NSMutableAttributedString实例就创建出来了。
接下来就要在drawTextInRect函数中开始绘制了。
普通视图坐标系原点在左上方,而QuartZ绘图的坐标系原点在左下方,所以我们先要调整坐标系。
CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetTextMatrix(context,CGAffineTransformIdentity);//重置 CGContextTranslateCTM(context,,self.bounds.size.height); //y轴高度 CGContextScaleCTM(context,1.0,-1.0);//y轴翻转
好了,可以开始绘制,因为"hello world"较短,一行就可以放下,那么可以这么绘制
CTLineRef line = CTLineCreateWithAttributedString(attributedText); CGContextSetTextPosition(context,,); CTLineDraw(line,context); CFRelease(line);
OK,就这么多搞定。
那如果文本很长,希望换行来显示怎么办?
那我们先要给attributedText添加些关于行相关属性。
下面都是基本的,可以根据自己绘制情况调整,扩充相应的变量。
CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping;//换行模式
CTTextAlignment alignment = kCTLeftTextAlignment;//对齐方式
float lineSpacing =2.0;//行间距
CTParagraphStyleSetting paraStyles[] = {
{.spec = kCTParagraphStyleSpecifierLineBreakMode,.valueSize = sizeof(CTLineBreakMode), .value = (const void*)&lineBreakMode},
{.spec = kCTParagraphStyleSpecifierAlignment,.valueSize = sizeof(CTTextAlignment), .value = (const void*)&alignment},
{.spec = kCTParagraphStyleSpecifierLineSpacing,.valueSize = sizeof(CGFloat), .value = (const void*)&lineSpacing},
};
CTParagraphStyleRef style = CTParagraphStyleCreate(paraStyles,);
[attributedText addAttribute:(NSString*)(kCTParagraphStyleAttributeName) value:(id)style range:NSMakeRange(,[text length])];
CFRelease(style);
下面就可以绘制了
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText); CGMutablePathRef path = CGPathCreateMutable(); CGPathAddRect(path,NULL,self.bounds); CTFrameRef frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(,),path,NULL); CGContextSetTextPosition(context,,); CTFrameDraw(frame,context); CFRelease(framesetter); CFRelease(frame);
这样绘制出来的可以自适应换行了,或者可以做的复杂点,通过上面获取的CTFrameRef frame来得到CTLineRef绘制
CFArrayRef lines = CTFrameGetLines(frame);
int lineNumber = CFArrayGetCount(lines);
CGPoint lineOrigins[lineNumber];
CTFrameGetLineOrigins(frame,CFRangeMake(,lineNumber), lineOrigins);
for(int lineIndex = ;lineIndex < lineNumber;lineIndex++){
CGPoint lineOrigin = lineOrigins[lineIndex];
CTLineRef line = CFArrayGetValueAtIndex(lines,lineIndex);
CGContextSetTextPosition(context,lineOrigin.x,lineOrigin.y);
CTLineDraw(line,context);
}
但这样绘制方法有个问题,就是即使行间距设为0.0(不能为负值)仍是比较分散的。如果希望更进一步控制好行间距,那自己就一行一行计算位置画
float lineHeight = ; //行高
BOOL drawFlag = YES;//是否绘制
int lineCount = ;//行数
CFIndex currentIndex = ;//绘制计数
CTTypesetterRef typeSetter = CTTypesetterCreateWithAttributedString((CFAttributedStringRef)attributedText);
float fontAscender = MAX(font_hello.ascender,font_world.ascender);
float y = self.bounds.origin.y+self.bounds.size.height-fontAscender;
while(drawFlag)
{
CFIndex lineLength = CTTypesetterSuggestLineBreak(typeSetter,currentIndex,self.bounds.size.width);
CFRange lineRange = CFRangeMake(currentIndex,lineLength);
CTLineRef line = CTTypesetterCreateLine(typeSetter,lineRange);
float x = CTLineGetPenOffsetForFlush(line,,self.bounds.size.width);
CGContextSetTextPosition(context,x,y);
CTLineDraw(line,context);
if(currentIndex + lineLength >= [text length]){
drawFlag = NO;
}
CFRelease(line);
count++;
y -=lineHeight;
currentIndex += lineLength;
}
CFRelease(typeSetter);
到这里的时候,又有个问题,就是用同样的字体和字号,绘制的字总是会比UILabel显示的粗些。
查看kCTStrokeWidthAttributeName属性发现默认已经为0.0,但这个值是可以为负值,那就变通的解决这个问题。
在attributedText里添加两个属性值
CGFloat widthValue = -1.0; CFNumberRef strokeWidth = CFNumberCreate(NULL,kCFNumberFloatType,&widthValue); [attributedText addAttribute:(NSString*)(kCTStrokeWidthAttributeName) value:(id)strokeWidth range:NSMakeRange(,[text length])]; [attributedText addAttribute:(NSString*)(kCTStrokeColorAttributeName) value:(id)[[UIColor whiteColor]CGColor] range:NSMakeRange(,[text length])];
这样绘制出来的字就细致些
CoreText的绘制流程-转的更多相关文章
- 深入理解 Android 之 View 的绘制流程
概述 本篇文章会从源码(基于Android 6.0)角度分析Android中View的绘制流程,侧重于对整体流程的分析,对一些难以理解的点加以重点阐述,目的是把View绘制的整个流程把握好,而对于特定 ...
- Android应用层View绘制流程与源码分析
1 背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...
- Android中View绘制流程以及invalidate()等相关方法分析
[原文]http://blog.csdn.net/qinjuning 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简 ...
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/16330267 在上一篇文章中,我带着大家一起剖析了一下LayoutInflater ...
- UE3:SkeletalMesh的绘制流程
[目标] SkeletalMesh的绘制流程 [思路] 1 顶点缓冲流 静态数据流向 动态数据流向(紫红色箭头) 2 FGPUSkinVertexFactory.ShaderDataType.Bone ...
- 自定义View_1_关于View,ViewGroup的测量和绘制流程
自定义View(1) ------ 关于View,ViewGroup的测量和绘制流程 在Android当中,自定义控件属于比较高级的知识体系,今天我们就一起研究研究关于自定义View的那点事,看看它到 ...
- View (三) 视图绘制流程完全解析
相 信每个Android程序员都知道,我们每天的开发工作当中都在不停地跟View打交道,Android中的任何一个布局.任何一个控件其实都是直接或间 接继承自View的,如TextView.Butto ...
- OpenGL ES学习笔记(一)——基本用法、绘制流程与着色器编译
首先声明下,本文为笔者学习<OpenGL ES应用开发实践指南(Android卷)>的笔记,涉及的代码均出自原书,如有需要,请到原书指定源码地址下载. 在Android.iOS等移动平台上 ...
- Android View绘制流程
框架分析 在之前的下拉刷新中,小结过触屏消息先到WindowManagerService(Wms)然后顺次传递给ViewRoot(派生自Handler),经decor view到Activity再传递 ...
随机推荐
- 如何将Python的py程序打包成跨平台的exe文件
在编写了自己的第一个可以爬写网页源代码的程序之后,发现如果在没有安装了pythonLDLE程序的计算机上根本就跑不出来.所以开始寻找可以将程序打包成跨平台运行的exe文件. 经过自己费力的谷歌没有一个 ...
- 工作总结 使用html模板发邮件 前面空一大块
HTML邮件的本质其实是发送了一个html页面.邮件的空白必然是页面的空白,所以你要找到你发送邮件的html模板所在,然后去掉空白即可,如果这是一个公共文件,需要注意你往往用的只是你的部分,很大程度还 ...
- [更新Github地址]python学习,自己写了个简单聊天工具mychat
最近在学习python,自己写了个最最简单的聊天工具mychatv0.1. 第一版,完成基本的聊天功能. GUI用的是自带的TKinter,用到的模块主要就是socket(网络编程), thread( ...
- 导入别人的Android项目,提示 /Libs/gen already exists but is not a source folder. Convert to a source folder or rename it
解决方法: 遇到这个问题的解决方法: 1. 右键点击工程,选择 "Properties" 2. 选择左边的 "Java Build Path" 3. 打开 &q ...
- md5 js
js-md5 - npm https://www.npmjs.com/package/js-md5 var rotateLeft = function(lValue, iShiftBits) { re ...
- 在调试状态查看DateTable里的数据信息
- javaScript改变HTML
改变HTML输出流: 在JavaScript中,document.write() 可用于直接向HTML输出流写内容 <!DOCTYPE html> <html> <bod ...
- intellij IDEA破解
方法1 填入下面的license server: http://intellij.mandroid.cn/ http://idea.imsxm.com/ http://idea.iteblog.com ...
- Jackson 框架的高阶应用
Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架.Jackson 社 区相对比较活跃,更新速度也比较快, 从 Github 中的统计来看,Jackson ...
- YTU 2895: H--唱歌的鸟儿
2895: H--唱歌的鸟儿 时间限制: 1 Sec 内存限制: 128 MB 提交: 26 解决: 10 题目描述 烟大东门有一棵大杨树,树上经常会有很多鸟儿飞来飞去.春天来了,学生物的小姜发现 ...