CA*Layer(CAShapeLayer--CATextLayer)

CAShapeLayer

CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。你指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图 形,最后CAShapeLayer就自动渲染出来了。当然,你也可以用Core Graphics直接向原始的CALyer的内容中绘制一个路径,相比直下,使用CAShapeLayer有以下一些优点:

  • 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
  • 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
  • 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉(如我们在第二章所见)。
  • 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。

(1).用CAShapeLayer绘制一个火柴人:

#import "DrawingView.h"

#import @interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController

- (void)viewDidLoad

{

  [super viewDidLoad];

  //create path

  UIBezierPath *path = [[UIBezierPath alloc] init];

  [path moveToPoint:CGPointMake(175, 100)];

  ?

  [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES];

  [path moveToPoint:CGPointMake(150, 125)];

  [path addLineToPoint:CGPointMake(150, 175)];

  [path addLineToPoint:CGPointMake(125, 225)];

  [path moveToPoint:CGPointMake(150, 175)];

  [path addLineToPoint:CGPointMake(175, 225)];

  [path moveToPoint:CGPointMake(100, 150)];

  [path addLineToPoint:CGPointMake(200, 150)];

  //create shape layer

  CAShapeLayer *shapeLayer = [CAShapeLayer layer];

  shapeLayer.strokeColor = [UIColor redColor].CGColor;

  shapeLayer.fillColor = [UIColor clearColor].CGColor;

  shapeLayer.lineWidth = 5;

  shapeLayer.lineJoin = kCALineJoinRound;

  shapeLayer.lineCap = kCALineCapRound;

  shapeLayer.path = path.CGPath;

  //add it to our view

  [self.containerView.layer addSublayer:shapeLayer];

}

@end

(2).我们创建圆角举行其实就是人工绘制单独的直线和弧度,但是事实上UIBezierPath有自动绘制圆角矩形的构造方法,下面这段代码绘制了一个有三个圆角一个直角的矩形:

//define path parameters

CGRect rect = CGRectMake(50, 50, 100, 100);

CGSize radii = CGSizeMake(20, 20);

UIRectCorner corners = UIRectCornerTopRight | UIRectCornerBottomRight | UIRectCornerBottomLeft;

//create path

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:radii];

CATextLayer

CATextLayer也要比UILabel渲染得快得多。很少有人知道在iOS 6及之前的版本,UILabel其实是通过WebKit来实现绘制的,这样就造成了当有很多文字的时候就会有极大的性能压力。而CATextLayer使 用了Core text,并且渲染得非常快

(1).用CATextLayer来实现一个UILabel

@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;

  [self.labelView.layer addSublayer:textLayer];

  //set text attributes

  textLayer.foregroundColor = [UIColor blackColor].CGColor;

  textLayer.alignmentMode = kCAAlignmentJustified;

  textLayer.wrapped = YES;

  //choose a font

  UIFont *font = [UIFont systemFontOfSize:15];

  //set layer font

  CFStringRef fontName = (__bridge CFStringRef)font.fontName;

  CGFontRef fontRef = CGFontCreateWithFontName(fontName);

  textLayer.font = fontRef;

  textLayer.fontSize = font.pointSize;

  CGFontRelease(fontRef);

  //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";

  //set layer text

  textLayer.string = text;

}

@end

如果你自习看这个文本,你会发现一个奇怪的地方:这些文本有一些像素化了。这是因为并没有以Retina的方式渲染,第二章提到了这个 contentScale属性,用来决定图层内容应该以怎样的分辨率来渲染。contentsScale并不关心屏幕的拉伸因素而总是默认为1.0。如果 我们想以Retina的质量来显示文字,我们就得手动地设置CATextLayer的contentsScale属性,如下:

   textLayer.contentsScale = [UIScreen mainScreen].scale;

这样就解决了这个问题(如图6.3)

(2).用NSAttributedString实现一个富文本标签。

@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

(3).UILabel的替代品

我们已经证实了CATextLayer比UILabel有着更好的性能表现,同时还有额外的布局选项并且在iOS 5上支持富文本。但是与一般的标签比较而言会更加繁琐一些。如果我们真的在需求一个UILabel的可用替代品,最好是能够在Interface Builder上创建我们的标签,而且尽可能地像一般的视图一样正常工作。

我们应该继承UILabel,然后添加一个子图层 CATextLayer并重写显示文本的方法。但是仍然会有由UILabel的-drawRect:方法创建的空寄宿图。而且由于CALayer不支持自 动缩放和自动布局,子视图并不是主动跟踪视图边界的大小,所以每次视图大小被更改,我们不得不手动更新子图层的边界。

我们真正想要的是一个用CATextLayer作为宿主图层的UILabel子类,这样就可以随着视图自动调整大小而且也没有冗余的寄宿图啦。

就 像我们在第一章『图层树』讨论的一样,每一个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 @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来创建基于不同图层的视图是一个简单可复用的方法。

时间: 2024-12-11 19:11:20

CA*Layer(CAShapeLayer--CATextLayer)的相关文章

CA*Layer(CATransformLayer--CAGradientLayer)

CATransformLayer CATransformLayer不同于普通的CALayer,因为它不能显示它自己的内容.只有当存在了一个能作用域子图层的变换它才真正存在.CATransformLayer并不平面化它的子图层,所以它能够用于构造一个层级的3D结构,比如我的手臂示例. 我们将通过旋转camara来解决图层平面化问题而不是像立方体示例代码中用的sublayerTransform.这是一个非常不错的技巧,但是只能作用域单个对象上,如果你的场景包含两个立方体,那我们就不能用这个技巧单独旋

CA*Layer(CAReplicatorLayer--)

CAReplicatorLayer (反射应用) 指定一个继承于UIView的ReflectionView,它会自动产生内容的反射效果: + (Class)layerClass//我们也可以通过重写View中的该方法来使其创建我们需要的layer类{    return [CAReplicatorLayer class];}- (void)setUp{    //configure replicator    CAReplicatorLayer *layer = (CAReplicatorLay

HDU 4374 One hundred layer(单调队列优化dp)

题意:有一个n*m的矩阵,每格有一个分数,一个人站在(1,x)位置,在每一行中,他只能朝一个方向走(向左或向右),且最多走t步,问走到最后第n行得到的最大分数. 思路:不难想到状态转移方程dp[i][j] = max(dp[i-1][k]+sum[i][j]-sum[i][k-1]),(k<j) 移项得 dp[i][j]-sum[i][j] = max(dp[i-1][k] - sum[i][k-1]); 方程右侧与i,j无关,所以可以用单调队列维护max(dp[i-1][k] - sum[i]

android图像处理系列之五--给图片添加边框(中)

前面一篇讲到给图片加边框的方式,只能给图片加一些有规则的边框,如果想加一些比较精美的效果,就有点麻烦了.下面就给出解决这个问题的思路. 思路是:一些比较精美的花边图片我们是很难用代码控制,就目前本人水平是达不到,不排除牛人,再说了PS那些效果都是程序员做出来,肯定有实现的方法,这可能就要涉及很复杂的图形学.扯远了,接来说怎么用那些精美的花边做为图片的边框.简单的方式是用两张图片叠加.最简单的一种是本文介绍的,用透明的PNG格式图片.因为Android是支持PNG图片处理的,而且PNG图片有透明度

iOS多线程开发(二)---线程管理

线程管理 线程管理包括创建,配置,退出三部分.主要包括创建线程的成本,线程创建,线程属性配置,线程主体入口函数编写,线程中断等 一,线程创建成本 1,为辅助线程分配的堆栈空间大小,便于系统和进程管理,以及为函数参数和局部变量分配空间 A,内核数据结构(kernel data structures)---大约1KB,This memory is used to store the thread data structures and attributes, much of which is all

苹果新的编程语言 Swift 语言进阶(十四)--扩展

Swift语言的扩展是为一个已经存在的类.结构.枚举类型添加新功能的一种方式,包括为不能存取源代码的那些已经存在的类型添加功能.        扩展类似于Objective-C语言中的类别,与类别不同的是Swift语言的扩展没有名字.        扩展能够为已存在类型:     1)增加计算属性和计算静态属性:     2)定义新的实例方法和类型方法:     3)提... 请转移到此位置继续阅读,谢谢! 苹果新的编程语言 Swift 语言进阶(十四)--扩展 苹果新的编程语言 Swift 语

iOS:实现图片的无限轮播(二)---之使用第三方库SDCycleScrollView

下载链接:github不断更新地址:https://github.com/gsdios/SDCycleScrollView 使用原理:采用UICollectionView的重用机制和循环滚动的方式实现图片的无限轮播,播放非常顺畅,解决了UISCrollView使用时从最后一张跳到第一张时的生硬状态. 主要类截图: SDCollectionViewCell:用来重用的item,即显示图片的视图: SDCycleScrollView: 对外提供的一个创建轮播器的接口类,使用者就是直接使用这个类来实现

visual c++ 2013进行MySQL编程(ODBC) -- (一) 套装安装

最近写了有些技术类文章了,只因为最近研究多了些东西,有一些项目用到了,所以自己记录一下,怕自己忘记,如果有哪位同学有自己的见解,可以邮件或者回复,技术类的探讨,不管对否,都是欢迎的. 操作之前,必须安装有Visual Studio 2013,其他的平台,我没有研究,我用的操作系统是Windows 8 Ultimate 64bit. 回到正题,vc数据库编程操作数据库Mysql,使用ODBC驱动接口. 而不是使用Mysql自带的C或者C++接口. 安装数据库,可以下面这个网址这里下载一个大的安装包

Android学习心得(16) --- Dex文件结构实例解析(2)

我在博客上发表一些我的Android学习心得,希望对大家能有帮助. 这一篇我们讲述一下通过一个实例来分析dex文件结构和组成. 参考Leb128数据类型 Android学习心得(5) --- dex数据类型LEB128 参考实例分析学习理解dex文件结构Android学习心得(15) --- Dex文件结构解析(1) 参考baksmali工具使用Android学习心得(4) --- MAC下smali文件编写与运行 1.编译 我们通过一个例子来分析dex文件的构成 创建一个Hello.java文