首页 > 学院 > 开发设计 > 正文

[iOSUI进阶-6.0]CALayer

2019-11-14 19:38:48
字体:
来源:转载
供稿:网友
A.基本知识
1.需要掌握的
  • CALayer的基本属性
  • CALayer和UIView的关系
  • position和anchorPoint的作用
 
2.概念
在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮、一个文本标签、一个文本输入框、一个图标等等,这些都是UIView

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

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

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

换句话说,UIView本身不具备显示的功能,是它内部的层才有显示功能
 
3.基本使用
通过操作CALayer对象,可以很方便地调整UIView的一些外观属性,比如:
阴影
圆角大小
边框宽度和颜色
… …

还可以给图层添加动画,来实现一些比较炫酷的效果
 
4.属性
(1)基本属性
边框颜色(CGColorRef类型)
@property CGColorRef borderColor;

边框宽度
@property CGFloat borderWidth;

圆角半径
@property CGColorRef borderColor;

内容(比如设置为图片CGImageRef)
@property(retain) id contents;
 
(2)transform3d key paths
Image(14)
 
  1 //  2 //  ViewController.m  3 //  CALayerTest  4 //  5 //  Created by hellovoidworld on 15/1/14.  6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved.  7 //  8   9 #import "ViewController.h" 10  11 @interface ViewController () 12 @property (weak, nonatomic) IBOutlet UIView *orangeView; 13  14 @end 15  16 @implementation ViewController 17  18 - (void)viewDidLoad { 19     [super viewDidLoad]; 20     // Do any additional setup after loading the view, typically from a nib. 21     22 //    [self testBasicLayer]; 23     24 //    [self testOrangeView]; 25     26 //    [self testImageView]; 27     28     [self testTransform]; 29 } 30  31 /** 基本属性 */ 32 - (void) testBasicLayer { 33     //  创建一个图层 34     CALayer *layer = [[CALayer alloc] init]; 35     // 设置图层边框颜色 36     layer.borderColor = [UIColor blueColor].CGColor; 37     //  设置图层边框粗细,边框是占据view内的尺寸的 38     layer.borderWidth = 10; 39     // 40     layer.frame = CGRectMake(50, 100, 200, 200); 41     layer.backgroundColor = [UIColor redColor].CGColor; 42     layer.cornerRadius = 15; 43     //    layer.masksToBounds = YES; 44     45     46     //    UIImage *image = [UIImage imageNamed:@"M3Mini"]; 47     //    layer.contents = (id)image.CGImage; 48     49     layer.shadowColor = [UIColor blackColor].CGColor; 50     layer.shadowOffset = CGSizeMake(50, 50); 51     layer.shadowOpacity = 0.5; 52     53     [self.view.layer addSublayer:layer]; 54 } 55  56 /** UIView的圆角+阴影 */ 57 - (void) testOrangeView { 58     self.orangeView.layer.shadowColor = [UIColor blackColor].CGColor; 59     self.orangeView.layer.shadowOffset = CGSizeMake(20, 20); 60     self.orangeView.layer.shadowOpacity = 0.5; 61     self.orangeView.layer.cornerRadius = 15; 62     63     self.orangeView.layer.borderWidth = 10; 64 } 65  66 /** ImageView的圆角+阴影效果 */ 67 - (void) testImageView { 68     // 用于产生阴影的辅助view,作为父view,显示在底层 69     UIView *shadowView = [[UIView alloc] init]; 70     shadowView.frame = CGRectMake(50, 80, 100, 64); 71     shadowView.layer.cornerRadius = 10; 72     shadowView.backgroundColor = [UIColor redColor]; 73     74     // 产生阴影 75     shadowView.layer.shadowColor = [UIColor blueColor].CGColor; 76     shadowView.layer.shadowOffset = CGSizeMake(10, 10); 77     shadowView.layer.shadowOpacity = 0.7; 78     79     // 用于显示圆角内容的imageView,作为子view,显示在表层 80     UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"M3Mini"]]; 81     imageView.frame = shadowView.bounds; 82     imageView.layer.cornerRadius = shadowView.layer.cornerRadius ; 83     84     // 圆角裁剪 85     imageView.layer.masksToBounds = YES; 86     87     // “合成”两个view 88     [shadowView addSubview:imageView]; 89     90     // 添加到屏幕 91     [self.view addSubview:shadowView]; 92 } 93  94 /** 3D transform */ 95 - (void) testTransform { 96     UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"M3Mini"]]; 97     imageView.frame = CGRectMake(50, 80, 100, 64); 98     99     // 1.从3D的x、y、z轴三个向量的缩放100 //    imageView.layer.transform = CATransform3DMakeScale(2, 2, 0);101    102     // 2.沿着x=1,y=1,z=0的向量为轴旋转103 //    imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 1, 1, 0);104    105     // 3.使用key paths设置106     // 3.1使用整个transform对象来设置,3D向量旋转107 //    NSValue *value = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI_4, 1, 1, 0)];108 //    [imageView.layer setValue:value forKeyPath:@"transform"];109    110     // 3.2直接利用rotation来设置,2D旋转(跟view的transform旋转一样)111     [imageView.layer setValue:@(M_PI_4) forKeyPath:@"transform.rotation"];112    113     [self.view addSubview:imageView];114 }115  116 - (void)didReceiveMemoryWarning {117     [super didReceiveMemoryWarning];118     // Dispose of any resources that can be recreated.119 }120 121 @end
 
 
#mark:
1.在同一个View上,使用了layer.masksToBounds=YES;的时候会屏蔽掉阴影效果;如果要同时使用。(注意,同时使用圆角和阴影是允许的)
例如: 要同时实现一个UIImageView的圆角和阴影效果,由于图片所在图层不是UIImageView的主layer,设置的圆角并不能影响到图片(同时阴影也是没有圆角的),所以必须要使用masksToBounds才能实现圆角。
 
单纯同时使用圆角剪裁和阴影:
 1 - (void) testImageView { 2     UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"M3Mini"]]; 3     imageView.frame = CGRectMake(50, 80, 100, 64); 4     imageView.layer.cornerRadius = 10; 5     imageView.layer.masksToBounds = YES; 6     7     imageView.layer.shadowColor = [UIColor blueColor].CGColor; 8     imageView.layer.shadowOffset = CGSizeMake(10, 10); 9     imageView.layer.shadowOpacity = 0.7;10    11     [self.view addSubview:imageView];12 }
 
没有阴影,因为被masksToBounds剪掉了:
Image(15)
 
解决:建议使用两个view,用带阴影的view(辅助view)和使用masksToBounds的view(需要显示内容的view)作为父子控件。
 1 - (void) testImageView { 2     // 用于产生阴影的辅助view,作为父view,显示在底层 3     UIView *shadowView = [[UIView alloc] init]; 4     shadowView.frame = CGRectMake(50, 80, 100, 64); 5     shadowView.layer.cornerRadius = 10; 6     shadowView.backgroundColor = [UIColor redColor]; 7     8     // 产生阴影 9     shadowView.layer.shadowColor = [UIColor blueColor].CGColor;10     shadowView.layer.shadowOffset = CGSizeMake(10, 10);11     shadowView.layer.shadowOpacity = 0.7;12    13     // 用于显示圆角内容的imageView,作为子view,显示在表层14     UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"M3Mini"]];15     imageView.frame = shadowView.bounds;16     imageView.layer.cornerRadius = shadowView.layer.cornerRadius ;17    18     // 圆角裁剪19     imageView.layer.masksToBounds = YES;20    21     // “合成”两个view22     [shadowView addSubview:imageView];23    24     // 添加到屏幕25     [self.view addSubview:shadowView];26 }
 
Image(16)
 
 
5.关于CALayer的疑问
首先
CALayer是定义在QuartzCore框架中的
CGImageRef、CGColorRef两种数据类型是定义在CoreGraphics框架中的
UIColor、UIImage是定义在UIKit框架中的

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

为了保证可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef
 
6.如何选择UIView和CALayer
通过CALayer,就能做出跟UIImageView一样的界面效果

既然CALayer和UIView都能实现相同的显示效果,那究竟该选择谁好呢?
其实,对比CALayer,UIView多了一个事件处理的功能。也就是说,CALayer不能处理用户的触摸事件,而UIView可以
所以,如果显示出来的东西需要跟用户进行交互的话,用UIView;如果不需要跟用户进行交互,用UIView或者CALayer都可以
当然,CALayer的性能会高一些,因为它少了事件处理的功能,更加轻量级
 
7. position & anchorPoint
CALayer有2个非常重要的属性:position和anchorPoint

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

@property CGPoint anchorPoint;
称为“定位点”、“锚点”
决定着CALayer身上的哪个点会在position属性所指的位置
以自己的左上角为原点(0, 0)
它的x、y取值范围都是0~1,默认值为(0.5, 0.5)
 
1     CALayer *layer = [[CALayer alloc] init];2     layer.backgroundColor = [UIColor redColor].CGColor;3     layer.frame = CGRectMake(0, 0, 100, 100);4     layer.position = CGPointMake(50, 50);5     // anchorPoint默认是(0.5,0.5),即中心点6     layer.anchorPoint = CGPointMake(0,0);7   8     [self.view.layer addSublayer:layer];
 
Image(17)
 
8.隐式动画
每一个UIView内部都默认关联着一个CALayer,我们可用称这个Layer为Root Layer(根层)

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

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

列举几个常见的Animatable Properties:
bounds:用于设置CALayer的宽度和高度。修改这个属性会产生缩放动画
backgroundColor:用于设置CALayer的背景色。修改这个属性会产生背景色的渐变动画
position:用于设置CALayer的位置。修改这个属性会产生平移动画
 
可以通过动画事务(CATransaction)关闭默认的隐式动画效果
1 [CATransaction begin];2 [CATransaction setDisableActions:YES];3 self.myview.layer.position = CGPointMake(10, 10);4 [CATransaction commit];
 
1 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {2     // 缩放3     self.layer.frame = CGRectMake(self.layer.position.x, self.layer.position.y, 200, 200);4     // 位移5     self.layer.position = CGPointMake(200, 200);6     // 背景色7     self.layer.backgroundColor = [UIColor blueColor].CGColor;8 }
 
Image(18)
 
10.自定义图层
有两种方法:自己创建layer类和使用代理
(1)创建继承CALayer的类,并实现drawInContent:方法来绘画
 1 // 2 //  HVWLayer.m 3 //  CALayerTest 4 // 5 //  Created by hellovoidworld on 15/1/14. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWLayer.h"10 11 @implementation HVWLayer12 13 14 /**15 * 必须调用一次layer的setNeedDisplay方法,才能触发此方法16 */17 - (void)drawInContext:(CGContextRef)ctx {18     NSLog(@"%@ - drawInContext", [self class]);19    20     // 绿色21     CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);22     // 矩形23     CGContextFillRect(ctx, CGRectMake(50, 50, 100, 100));24     CGContextFillPath(ctx);25 }26 27 @end
 
viewController:
1 - (void) diyLayer1 {2     HVWLayer *layer = [[HVWLayer alloc] init];3     layer.frame = CGRectMake(80, 80, 200, 300);4     layer.backgroundColor = [UIColor grayColor].CGColor;5     // 必须调用重绘,才能触发layer的drawInContent:方法6     [layer setNeedsDisplay];7    8     [self.view.layer addSublayer:layer];9 }
 
Image(19)
 
(2)使用layer的delegate
viewController:
 1 - (void) diyLayer2 { 2     CALayer *layer = [[CALayer alloc] init]; 3     layer.bounds = CGRectMake(0, 0, 200, 300); 4     layer.anchorPoint = CGPointZero; 5     layer.backgroundColor = [UIColor grayColor].CGColor; 6     layer.delegate = self; 7     8     // 必须要进行重绘 9     [layer setNeedsDisplay];10    11     [self.view.layer addSublayer:layer];12 }13  14 #pragma mark - layer代理方法15 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {16     // 绿色17     CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);18     // 矩形19     CGContextFillRect(ctx, CGRectMake(30, 30, 100, 100));20     CGContextFillPath(ctx);21 }

 

Image(20)
 
 
11.View和layer的调用关系
(1)view的layer加载顺序
  • view准备一个图层上下文Layer Context Ref
  • 调用[view.layer.delegate drawLayer:inContext:],传入上下文
  • 在drawLayer:inContext:内会调用view的drawRect:方法
  • 所以drawRect:内实现的绘图方法,会画到layer上
  • 系统把layer上的内容描绘到屏幕,完成view的显示
 
view: (创建了一个红圆)
 1 // 2 //  HVWView.m 3 //  CALayerTest 4 // 5 //  Created by hellovoidworld on 15/1/14. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWView.h"10 11 @implementation HVWView12 13 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {14     NSLog(@"%@ - drawLayer", [self class]);15     [super drawLayer:layer inContext:ctx];16 }17 18 - (void)drawRect:(CGRect)rect {19     NSLog(@"%@ - drawRect", [self class]);20     CGContextRef ctx = UIGraphicsGetCurrentContext();21     CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);22     CGContextFillEllipseInRect(ctx, CGRectMake(0, 0, 100, 100));23     CGContextFillPath(ctx);24 }25 26 @end
 
layer:(创建一个绿矩形)
 1 // 2 //  HVWLayer.m 3 //  CALayerTest 4 // 5 //  Created by hellovoidworld on 15/1/14. 6 //  Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8  9 #import "HVWLayer.h"10 11 @implementation HVWLayer12 13 14 - (void)drawInContext:(CGContextRef)ctx {15     NSLog(@"%@ - drawInContext", [self class]);16    17     // 绿色18     CGContextSetRGBFillColor(ctx, 0, 1, 0, 1);19     // 矩形20     CGContextFillRect(ctx, CGRectMake(100, 100, 100, 100));21     CGContextFillPath(ctx);22 }23  24 @end
 
ViewController:
 1 - (void)viewDidLoad { 2     [super viewDidLoad]; 3     // Do any additional setup after loading the view, typically from a nib. 4     5     // 创建view 6     HVWView *hvwView = [[HVWView alloc] init]; 7     hvwView.backgroundColor = [UIColor grayColor]; 8     hvwView.frame = CGRectMake(50, 50, 250, 250); 9    10     // 创建layer11     HVWLayer *layer = [[HVWLayer alloc] init];12     layer.frame = CGRectMake(50, 50, 250, 250);13     layer.backgroundColor = [UIColor blueColor].CGColor;14    15     // 必须要重绘才能调用layer的drawInContext16     [layer setNeedsDisplay];17    18     // 配置layer到view,会设置layer的delegate就是那个view19     [hvwView.layer addSublayer:layer];20    21     [self.view addSubview:hvwView];22 //    [self.view.layer addSublayer:layer];23 }
 
Image(21)
 
consol:
2015-01-14 21:59:45.558 CALayerTest3[3246:115305] HVWView - drawLayer
2015-01-14 21:59:45.560 CALayerTest3[3246:115305] HVWView - drawRect
2015-01-14 21:59:45.561 CALayerTest3[3246:115305] HVWLayer - drawInContext
 

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表