UILable 庫

https://github.com/AliSoftware/OHAttributedLabel


UILabel是經常使用的一種控件,iOS上的UILabel已經能很好滿足一些需求。比如設置對齊方式,換行模式等等。

但如果需求是需要一串字符中不同的字符顏色,字體都單獨設置,UILabel就無法滿足了。那就自己來做個富文本Label好了。

先創建繼承UILabel的AttributedLabel.h,AttributedLabel.m文件,重載UILabeld -(void)drawTextInRect:(CGRect)rect方法,我們的文本繪製就放在這個函數中。

使用coreText進行文本繪製,需要在工程中添加CoreText.framework,然後在AttributedLabel.m裏import<CoreText/CoreText.h>就可以使用了。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])];
這樣繪製出來的字就細緻些。

好了,沿着這條線的內容大致說完,有興趣的自己敲一敲吧

PS:我是試用MarsEdit這個離線工具時寫的這篇,貌似對代碼支持的不好,直接貼亂七八糟的,就就索性手敲了,沒測,有拼寫錯誤,縮進問題勿怪,歡迎討論交流

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章