[iOS Animation]-CALayer 專用圖層 富文本

富文本

iOS 6中,Apple給UILabel和其餘UIKit文本視圖添加了直接的屬性化字符串的支持,應該說這是一個很方便的特性。不過事實上從iOS3.2開始CATextLayer就已經支持屬性化字符串了。這樣的話,若是你想要支持更低版本的iOS系統,CATextLayer無疑是你向界面中增長富文本的好辦法,並且也不用去跟複雜的Core Text打交道,也省了用UIWebView的麻煩。 git

讓咱們編輯一下示例使用到NSAttributedString(見清單6.3).iOS 6及以上咱們能夠用新的NSTextAttributeName實例來設置咱們的字符串屬性,可是練習的目的是爲了演示在iOS 5及如下,因此咱們用了Core Text,也就是說你須要把Core Text framework添加到你的項目中。不然,編譯器是沒法識別屬性常量的。 github

圖6.4是代碼運行結果(注意那個紅色的下劃線文本) api

清單6.3 用NSAttributedString實現一個富文本標籤。 app

複製代碼
#import "DrawingView.h" #import <QuartzCore/QuartzCore.h> #import <CoreText/CoreText.h> @interface ViewController () @property (nonatomic, weak) IBOutlet UIView *labelView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //create a text layer CATextLayer *textLayer = [CATextLayer layer]; textLayer.frame = self.labelView.bounds; textLayer.contentsScale = [UIScreen mainScreen].scale; [self.labelView.layer addSublayer:textLayer]; //set text attributes textLayer.alignmentMode = kCAAlignmentJustified; textLayer.wrapped = YES; //choose a font UIFont *font = [UIFont systemFontOfSize:15]; //choose some text NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing \ elit. Quisque massa arcu, eleifend vel varius in, facilisis pulvinar \ leo. Nunc quis nunc at mauris pharetra condimentum ut ac neque. Nunc \ elementum, libero ut porttitor dictum, diam odio congue lacus, vel \ fringilla sapien diam at purus. Etiam suscipit pretium nunc sit amet \ lobortis";  //create attributed string NSMutableAttributedString *string = nil; string = [[NSMutableAttributedString alloc] initWithString:text]; //convert UIFont to a CTFont CFStringRef fontName = (__bridge CFStringRef)font.fontName; CGFloat fontSize = font.pointSize; CTFontRef fontRef = CTFontCreateWithName(fontName, fontSize, NULL); //set text attributes NSDictionary *attribs = @{ (__bridge id)kCTForegroundColorAttributeName:(__bridge id)[UIColor blackColor].CGColor, (__bridge id)kCTFontAttributeName: (__bridge id)fontRef }; [string setAttributes:attribs range:NSMakeRange(0, [text length])]; attribs = @{ (__bridge id)kCTForegroundColorAttributeName: (__bridge id)[UIColor redColor].CGColor, (__bridge id)kCTUnderlineStyleAttributeName: @(kCTUnderlineStyleSingle), (__bridge id)kCTFontAttributeName: (__bridge id)fontRef }; [string setAttributes:attribs range:NSMakeRange(6, 5)]; //release the CTFont we created earlier  CFRelease(fontRef); //set layer text textLayer.string = string; } @end
複製代碼

 

圖6.4

圖6.4 用CATextLayer實現一個富文本標籤。 佈局

行距和字距

有必要提一下的是,因爲繪製的實現機制不一樣(Core Text和WebKit),用CATextLayer渲染和用UILabel渲染出的文本行距和字距也不是不盡相同的。 性能

兩者的差別程度(由使用的字體和字符決定)總的來講挺小,可是若是你想正確的顯示普通便籤和CATextLayer就必定要記住這一點。 字體

UILabel的替代品

咱們已經證明了CATextLayer比UILabel有着更好的性能表現,同時還有額外的佈局選項而且在iOS 5上支持富文本。可是與通常的標籤比較而言會更加繁瑣一些。若是咱們真的在需求一個UILabel的可用替代品,最好是可以在Interface Builder上建立咱們的標籤,並且儘量地像通常的視圖同樣正常工做。 ui

咱們應該繼承UILabel,而後添加一個子圖層CATextLayer並重寫顯示文本的方法。可是仍然會有由UILabel的-drawRect:方法建立的空寄宿圖。並且因爲CALayer不支持自動縮放和自動佈局,子視圖並非主動跟蹤視圖邊界的大小,因此每次視圖大小被更改,咱們不得不手動更新子圖層的邊界。 this

咱們真正想要的是一個用CATextLayer做爲宿主圖層的UILabel子類,這樣就能夠隨着視圖自動調整大小並且也沒有冗餘的寄宿圖啦。 atom

就像咱們在第一章『圖層樹』討論的同樣,每個UIView都是寄宿在一個CALayer的示例上。這個圖層是由視圖自動建立和管理的,那咱們能夠用別的圖層類型替代它麼?一旦被建立,咱們就沒法代替這個圖層了。可是若是咱們繼承了UIView,那咱們就能夠重寫+layerClass方法使得在建立的時候能返回一個不一樣的圖層子類。UIView會在初始化的時候調用+layerClass方法,而後用它的返回類型來建立宿主圖層。

清單6.4 演示了一個UILabel子類LayerLabel用CATextLayer繪製它的問題,而不是調用通常的UILabel使用的較慢的-drawRect:方法。LayerLabel示例既能夠用代碼實現,也能夠在Interface Builder實現,只要把普通的標籤拖入視圖之中,而後設置它的類是LayerLabel就能夠了。

清單6.4 使用CATextLayer的UILabel子類:LayerLabel

複製代碼
#import "LayerLabel.h" #import <QuartzCore/QuartzCore.h> @implementation LayerLabel + (Class)layerClass { //this makes our label create a CATextLayer //instead of a regular CALayer for its backing layer return [CATextLayer class]; } - (CATextLayer *)textLayer { return (CATextLayer *)self.layer; } - (void)setUp { //set defaults from UILabel settings self.text = self.text; self.textColor = self.textColor; self.font = self.font; //we should really derive these from the UILabel settings too //but that's complicated, so for now we'll just hard-code them [self textLayer].alignmentMode = kCAAlignmentJustified;  [self textLayer].wrapped = YES; [self.layer display]; } - (id)initWithFrame:(CGRect)frame { //called when creating label programmatically if (self = [super initWithFrame:frame]) { [self setUp]; } return self; } - (void)awakeFromNib { //called when creating label using Interface Builder  [self setUp]; } - (void)setText:(NSString *)text { super.text = text; //set layer text [self textLayer].string = text; } - (void)setTextColor:(UIColor *)textColor { super.textColor = textColor; //set layer text color [self textLayer].foregroundColor = textColor.CGColor; } - (void)setFont:(UIFont *)font { super.font = font; //set layer font CFStringRef fontName = (__bridge CFStringRef)font.fontName; CGFontRef fontRef = CGFontCreateWithFontName(fontName); [self textLayer].font = fontRef; [self textLayer].fontSize = font.pointSize;  CGFontRelease(fontRef); } @end
複製代碼

 

若是你運行代碼,你會發現文本並無像素化,而咱們也沒有設置contentsScale屬性。把CATextLayer做爲宿主圖層的另外一好處就是視圖自動設置了contentsScale屬性。

在這個簡單的例子中,咱們只是實現了UILabel的一部分風格和佈局屬性,不過稍微再改進一下咱們就能夠建立一個支持UILabel全部功能甚至更多功能的LayerLabel類(你能夠在一些線上的開源項目中找到)。

若是你打算支持iOS 6及以上,基於CATextLayer的標籤可能就有有些侷限性。可是總得來講,若是想在app裏面充分利用CALayer子類,用+layerClass來建立基於不一樣圖層的視圖是一個簡單可複用的方法。

相關文章
相關標籤/搜索