iOS开发中的图层-CALayer-

来源:互联网 发布:手机淘宝卖家版登陆 编辑:程序博客网 时间:2024/06/05 16:54

CALayer

简介

  • 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮、一个文本标签、一个图标等等,这些都是UIView

  • UIView之所以能显示在屏幕上,完全是因为它内部的一个图层

  • 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView的layer属性可以访问这个层
    @property(nonatomic,readonly,retain) CALayer *layer;

  • 当UIView需要显示在屏幕上时,会调用drawRect:方法进行绘图,并且会将所有的内容绘制在自己的图层上,绘图完毕后,系统会将图层拷贝在屏幕上,于是完成了UIView的显示。

  • 换句话说,UIView本身不具备显示的功能,是它内部的层才有显示功能

CALayer的基本使用

  • 通过操作CALayer对象,可以很方便地调整UIView的一些外观属性,比如:

    阴影
    圆角大小
    边框宽度和颜色
    … …

  • 还可以给图层添加动画,来实现一些比较炫酷的效果

CALayer的属性

基本属性

宽度和高度@property CGRect bounds;位置(默认指中点,具体由anchorPoint决定)@property CGPoint position;锚点(x,y的范围都是0-1),决定了position的含义@property CGPoint anchorPoint;背景颜色(CGColorRef类型)@property CGColorRef backgroundColor;形变属性@property CATransform3D transform;边框颜色(CGColorRef类型)@property CGColorRef borderColor;边框宽度@property CGFloat borderWidth;内容(比如设置为图片CGImageRef)@property(retain) id contents;

通过Layer设置UIView控件外观

borderWidth 设置边框大小borderColor 设置边框颜色cornerRadius设置圆角//下面属性必须同时设置了阴影才有效果。shadowColor 设置阴影颜色shadowOffset 设置阴影偏差shadowOpacity 设置阴影不透明度,取值0~1。默认值为0。表示透明。

通过Layer设置UIImageView控件外观(注意跟UIView的区别)

  • 设置 UIImageView 的圆角必须同时执行下面两行代码
self.imageView.layer.cornerRadius = 10;self.imageView.layer.masksToBounds = YES;
  • 为什么普通的UIView 设置圆角一行就可以,UIImageView就需要两行?
    答:因为UIImageView显示的图片是通过它的layer的contents属性显示的。图片默认是填满整个layer的,self.imageView.layer.cornerRadius仅仅是设置了layer 为圆角,而 layer上面的图片并没有圆角,所以需要设置self.imageView.layer.masksToBounds为 YES,这样超出layer的图片内容就会被裁剪掉。

  • 设置UIImageView的阴影

self.imageView.layer.shadowColor = [UIColor orangeColor].CGColor;self.imageView.layer.shadowOffset = CGSizeMake(-10, -10);self.imageView.layer.shadowOpacity = 0.5;
  • 为什么UIImageView不能同时设置阴影和圆角?
    答:设置圆角时必须设置masksToBounds为YES。一旦设置masksToBounds为 YES,那么阴影就会被裁剪掉了。所以要想同时设置 UIImageView的圆角和阴影就只能通过其他方法。

通过Layer设置UIButton控件外观

    // 设置圆角    self.button.imageView.layer.masksToBounds = YES;    self.button.imageView.layer.cornerRadius = 10;    // 设置阴影    self.button.layer.shadowColor = [UIColor greenColor].CGColor;    self.button.layer.shadowOffset = CGSizeMake(10, 10);    self.button.layer.shadowOpacity = 0.5;
  • 要想同时设置按钮圆角和阴影,必须不能设置按钮的背景图片,只能通过 setImage:forState:来设置按钮图片,并且按钮的backgroundColor必须是clearColor。

layer的形变属性transform。

  • 缩放
    // x,y缩放
    self.customView.layer.transform = CATransform3DMakeScale(0.5, 0.5, 0);

  • 平移
    // x,y方向平移100,z方向不平移
    self.customView.layer.transform = CATransform3DMakeTranslation(100, 100, 0);

  • 旋转
    // 绕 x,y 夹角45°旋转
    self.customView.layer.transform = CATransform3DMakeRotation(M_PI_4, 1, 1, 0);

    // 绕z轴旋转
    self.customView.layer.transform=CATransform3DMakeRotation(M_PI_4, 0, 0, 1);

    //通过KVC来设置
    NSValue *value =[NSValue valueWithCATransform3D:CATransform3DMakeTranslation(100, 100, 0)];
    [self.customView.layer setValue:value forKeyPath:@”transform”];

    //如果是只需要设置在某一个方向上的移动,可以参考下面的代码
    //在x的方向上向左移动100
    [self.customView.layer setValue:@(100) forKeyPath:@”transform.translation.x”];

  • 可以传递哪些 key path,在官方文档搜索”CATransform3D key paths”

CALayer中的两个非常重要的属性:position和anchorPoint

  • position和anchorPoint属性都是CGPoint类型的

  • position可以用来设置CALayer在父层中的位置,它是以父层的左上角为坐标原点(0, 0)
    @property CGPoint position;

  • anchorPoint称为”定位点”,它决定着CALayer身上的哪个点会在position属性所指的位置。它的x、y取值范围都是0~1,默认值为(0.5, 0.5)
    @property CGPoint anchorPoint;

创建CALayer,添加到控制器的view的layer中

CALayer *myLayer = [CALayer layer];// 设置层的宽度和高度(100x100)myLayer.bounds = CGRectMake(0, 0, 100, 100);// 设置层的位置myLayer.position = CGPointMake(100, 100);// 设置层的背景颜色:红色myLayer.backgroundColor = [UIColor redColor].CGColor;// 添加myLayer到控制器的view的layer中[self.view.layer addSublayer:myLayer];

关于CALayer的疑惑

  • 首先

    CALayer是定义在QuartzCore框架中的
    CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的
    UIColor、UIImage是定义在UIKit框架中的

  • 其次

    QuartzCore框架和CoreGraphics框架是可以跨平台使用的,在iOS和Mac OS X上都能使用
    但是UIKit只能在iOS中使用

  • 为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef

UIView和CALayer的选择

  • 通过CALayer,就能做出跟UIImageView一样的界面效果

  • 既然CALayer和UIView都能实现相同的显示效果,那究竟该选择谁好呢?

    • 其实,对比CALayer,UIView多了一个事件处理的功能。也就是说,CALayer 不能处理用户的触摸事件,而UIView可以
      • 所以,如果显示出来的东西需要跟用户进行交互的话,用UIView;如果不需要跟用户进行交互,用UIView或者CALayer都可以
      • 当然,CALayer的性能会高一些,因为它少了事件处理的功能,更加轻量级

UIView和CALayer的其他关系

  • UIView可以通过subviews属性访问所有的子视图,类似地,CALayer也可以通过sublayers属性访问所有的子层

  • UIView可以通过superview属性访问父视图,类似地,CALayer也可以通过superlayer属性访问父层

这里写图片描述
如果两个UIView是父子关系,那么它们内部的CALayer也是父子关系。

隐式动画

  • 每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根层)

  • 所有的非Root Layer,也就是手动创建的CALayer对象,都存在着隐式动画

  • 什么是隐式动画?

    当对非Root Layer的部分属性进行修改时,默认会自动产生一些动画效果
    而这些属性称为Animatable Properties(可动画属性)

隐式动画属性

  • 当对非Root Layer的部分属性进行相应的修改时,默认会自动产生一些动画效果,这些属性称为Animatable Properties(可动画属性)。

    • 列举几个常见的Animatable Properties:

      bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画
      backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画
      position:用于设置CALayer的位置。修改这个属性会产生平移动画

    • 可以通过动画事务(CATransaction)关闭默认的隐式动画效果

//开启事务[CATransaction begin];//禁止执行隐式动画[CATransaction setDisableActions:YES];self.myview.layer.position = CGPointMake(10, 10);//提交事务[CATransaction commit];

自定义图层

创建一个CALayer的子类,然后覆盖drawInContext:方法,使用Quartz2D API进行绘图

  • // 在指定的图形上下文中绘制图层显示的内容。
-(void)drawInContext:(CGContextRef)ctx { CGContextSetRGBFillColor(ctx, 1, 0, 0, 1); // 不能使用UIkit框架的类设置颜色。 //[[UIColor orangeColor] set]; CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100)); CGContextFillPath(ctx); }
  • 在自定义layer中的-(void)drawInContext:方法不会自己调用,只能自己通过setNeedDisplay方法调用

设置CALayer的delegate,然后让delegate实现drawLayer:inContext:方法,当CALayer需要绘图时,会调用delegate的drawLayer:inContext:方法进行绘图。

  • //在指定的图形上下文中给指定图层绘制内容。
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { CGContextSetRGBFillColor(ctx, 1, 0, 0, 1); CGContextAddEllipseInRect(ctx, CGRectMake(0, 0, 100, 100)); CGContextFillPath(ctx);}
  • 需要手动调用setNeedDisplay方法触发该方法的调用。

官方文档简介

  • 关于layer,官方文档有几句话需要知道的。

    If the layer object was created by a view, the view typically assigns itself as the layer’s delegate automatically, and you should not change that relationship. For layers you create yourself, you can assign a delegate object and use that object to provide the contents of the layer dynamically and perform other tasks.如果一个layer是被一个view创建的,那么这个view通常会自动被赋值给这个layer的delegate属性(也就算说这个view就是这个layer的代理)。你不应该改变这种关系。对于你自己创建的layer,你可以赋值一个代理对象用来动态提供layer的内容和执行其他的任务。
  • 关于layer的delegate属性,官方一句话需要谨记:
    In iOS, if the layer is associated with a UIView object, this property must be set to the view that owns the layer.
    意思是说:在ios中,如果一个layer已经关联了一个view对象。那么layer的delegate属性必须设置为这个拥有这个layer的view本身。

view的完整显示过程总结

  • view.layer会准备一个Layer Graphics Contex(图层类型的上下文)
  • 调用view.layer.delegate(view)的drawLayer:inContext:,并传入刚才准备好的上下文
  • view的drawLayer:inContext:方法内部又会调用view的drawRect:方法
  • view就可以在drawRect:方法中实现绘图代码, 所有东西最终都绘制到view.layer上面
  • 系统再将view.layer的内容拷贝到屏幕, 于是完成了view的显示
0 0
原创粉丝点击