• 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(0,5)];
    
    [attributedText addAttribute:(NSString*)(kCTForegroundColorAttributeName) value:(id)[[UIColor redColor]CGColor] range:NSMakeRange(6,5)];
    
    CTFontRef  font_hello = CTFontCreateWithName((CFStringRef)@"Helvetica",16,NULL);
    
    CTFontRef  font_world = CTFontCreateWithName((CFStringRef)@"GillSans",20,NULL);
    
    [attributedText addAttribute: (NSString*)(kCTFontAttributeName) value:(id)font_hello range:NSMakeRange(0,5)];
    
    [attributedText addAttribute: (NSString*)(kCTFontAttributeName) value:(id)font_world range:NSMakeRange(6,5)];


    这样,一个包含简单绘制属性的NSMutableAttributedString实例就创建出来了。

    接下来就要在drawTextInRect函数中开始绘制了。

    普通视图坐标系原点在左上方,而QuartZ绘图的坐标系原点在左下方,所以我们先要调整坐标系。

    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetTextMatrix(context,CGAffineTransformIdentity);//重置
    
    CGContextTranslateCTM(context,0,self.bounds.size.height); //y轴高度
    
    CGContextScaleCTM(context,1.0,-1.0);//y轴翻转


    好了,可以开始绘制,因为"hello world"较短,一行就可以放下,那么可以这么绘制

    CTLineRef line = CTLineCreateWithAttributedString(attributedText);
    
    CGContextSetTextPosition(context,0,0);
    
    CTLineDraw(line,context);
    
    CFRelease(line);


    OK,就这么多搞定。

    那如果文本很长,希望换行来显示怎么办?

    那我们先要给attributedText添加些关于行相关属性。

    下面都是基本的,可以根据自己绘制情况调整,扩充相应的变量。

    CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrapping;//换行模式
    
    CTTextAlignment alignment = kCTLeftTextAlignment;//对齐方式
    
    float lineSpacing =2.0;//行间距
    
    CTParagraphStyleSetting paraStyles[3] = {
    
    {.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,3);
    
    [attributedText addAttribute:(NSString*)(kCTParagraphStyleAttributeName) value:(id)style range:NSMakeRange(0,[text length])];
    
    CFRelease(style);
    
    下面就可以绘制了
    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText);
    
    CGMutablePathRef path = CGPathCreateMutable();
    
    CGPathAddRect(path,NULL,self.bounds);
    
    CTFrameRef frame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0),path,NULL);
    
    CGContextSetTextPosition(context,0,0);
    
    CTFrameDraw(frame,context);
    
    CFRelease(framesetter);
    
    CFRelease(frame);
    
    

    这样绘制出来的可以自适应换行了,或者可以做的复杂点,通过上面获取的CTFrameRef frame来得到CTLineRef绘制

    CFArrayRef lines = CTFrameGetLines(frame);
    
    int lineNumber = CFArrayGetCount(lines);
    
    CGPoint lineOrigins[lineNumber];
    
    CTFrameGetLineOrigins(frame,CFRangeMake(0,lineNumber), lineOrigins);
    
    for(int lineIndex = 0;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 = 20; //行高
    
    BOOL drawFlag = YES;//是否绘制
    
    int lineCount = 0//行数
    
    CFIndex currentIndex = 0;//绘制计数
    
    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,0,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(0,[text length])];
    
    [attributedText addAttribute:(NSString*)(kCTStrokeColorAttributeName) value:(id)[[UIColor whiteColor]CGColor] range:NSMakeRange(0,[text length])];


    这样绘制出来的字就细致些

  • 相关阅读:
    【codevs1949】兔兔与蛋蛋的游戏
    【codevs1775】那些年
    【codevs3153】取石子
    [codevs1909]英语 博弈论
    spfa及slf优化
    [baoj3224]普通平衡树
    恋爱路上的几个叉
    考研之如何联系导师
    《C++程序设计语言(特别版)》忠告(advice)部分
    一位程序猿送给女朋友的礼物
  • 原文地址:https://www.cnblogs.com/endtel/p/4847446.html
Copyright © 2020-2023  润新知