使用coreText进行文本绘制,需要在工程中添加CoreText.framework,然后在AttributedLabel.m里import就可以使用了。coreText负责绘制,那绘制的内容和属性则要靠NSAttributedString来存储,如果属性具有不确定性,可以使用NSMutableAttributedStrin g,方便后面添加属性。 先来看下如何创建一个具有两个颜色,两种字体的“hello world”的NSMutableAttributedStrin g实例。 NSString *text = @"hello word"; NSMutableAttributedStrin g *attributedText = [[NSMutableAttributedStrin g alloc] initWithString:text]; [attributedText addAttribute:(NSString*)(kCTForegroundColorAttrib uteName) value:(id)[[UIColor blueColor]CGColor] range:NSMakeRange(0,5)]; [attributedText addAttribute:(NSString*)(kCTForegroundColorAttrib uteName) 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)]; 这样,一个包含简单绘制属性的NSMutableAttributedStrin g实例就创建出来了。 接下来就要在drawTextInRect函数中开始绘制了。 普通视图坐标系原点在左上方,而QuartZ绘图的坐标系原点在左下方,所以我们先要调整坐标系。 CGContextRef context = UIGraphicsGetCurrentCont ext(); CGContextSetTextMatrix(context,CGAffineTransformIdentit y);//重置 CGContextTranslateCTM(context,0,self.bounds.size.height); //y轴高度 CGContextScaleCTM(context,1.0,-1.0);//y轴翻转 好了,可以开始绘制,因为"hello world"较短,一行就可以放下,那么可以这么绘制 CTLineRef line = CTLineCreateWithAttribut edString(attributedText); CGContextSetTextPosition (context,0,0); CTLineDraw(line,context); CFRelease(line); OK,就这么多搞定。 那如果文本很长,希望换行来显示怎么办? 那我们先要给attributedText添加些关于行相关属性。 下面都是基本的,可以根据自己绘制情况调整,扩充相应的变量。 CTLineBreakMode lineBreakMode = kCTLineBreakByWordWrappi ng;//换行模式 CTTextAlignment alignment = kCTLeftTextAlignment;//对齐方式 float lineSpacing =2.0;//行间距 CTParagraphStyleSetting paraStyles[3] = { {.spec = kCTParagraphStyleSpecifi erLineBreakMode,.valueSize = sizeof(CTLineBreakMode), .value = (const void*)&lineBreakMode}, {.spec = kCTParagraphStyleSpecifi erAlignment,.valueSize = sizeof(CTTextAlignment), .value = (const void*)&alignment}, {.spec = kCTParagraphStyleSpecifi erLineSpacing,.valueSize = sizeof(CGFloat), .value = (const void*)&lineSpacing}, }; CTParagraphStyleRef style = CTParagraphStyleCreate(paraStyles,3); [attributedText addAttribute:(NSString*)(kCTParagraphStyleAttribu teName) value:(id)style range:NSMakeRange(0,[text length])]; CFRelease(style); 下面就可以绘制了 CTFramesetterRef framesetter = CTFramesetterCreateWithA ttributedString((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 = CTTypesetterCreateWithAt tributedString((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 = CTTypesetterSuggestLineB reak(typeSetter,currentIndex,self.bounds.size.width); CFRange lineRange = CFRangeMake(currentIndex,lineLength); CTLineRef line = CTTypesetterCreateLine(typeSetter,lineRange); float x = CTLineGetPenOffsetForFlu sh(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显示的粗些。 查看kCTStrokeWidthAttributeN ame属性发现默认已经为0.0,但这个值是可以为负值,那就变通的解决这个问题。 在attributedText里添加两个属性值 CGFloat widthValue = -1.0; CFNumberRef strokeWidth = CFNumberCreate(NULL,kCFNumberFloatType,&widthValue); [attributedText addAttribute:(NSString*)(kCTStrokeWidthAttributeN ame) value:(id)strokeWidth range:NSMakeRange(0,[text length])]; [attributedText addAttribute:(NSString*)(kCTStrokeColorAttributeN ame) value:(id)[[UIColor whiteColor]CGColor] range:NSMakeRange(0,[text length])]; 这样绘制出来的字就细致些 |