[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来建立基于不一样图层的视图是一个简单可复用的方法。

相关文章
相关标签/搜索