CALayer精讲

前言

CALayer包含在QuartzCore框架中,这是一个跨平台的框架,既可以用在iOS中又可以用在Mac OS X中。后面要学Core Animation就应该先学好Layer(层)。

我们看一下UIViewLayer之间的关系图(图片来源于网络):

我们知道,UIView有一个属性layer,这个是在视图创建时就会自动创建一个图层。想要呈现出来,就需要到Layer。层是可以放很多个子层的,也就可以实现多种多样的效果。

CALayer关键属性说明

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

// 与UIView的bounds类似的,获取或设置图层的大小

@property CGRect bounds;

/* The position in the superlayer that the anchor point of the layer‘s

* bounds rect is aligned to. Defaults to the zero point. Animatable. */

// 获取或设置在父图层中对齐位置,默认为(0,0),也就是左上角。

// 这个属性与UIView的center属性类似,不过在图层中使用的是position

// 这里关系到锚点,关于锚点在游戏世界里是非常关键的概念

// 支持隐式动画

@property CGPoint position;

/* The Z component of the layer‘s position in its superlayer. Defaults

* to zero. Animatable. */

// 层与层之间有上下层的关系,设置Z轴方向的值,可以指定哪个层在上,哪个层在下

// 支持隐式动画

@property CGFloat zPosition;

/* Defines the anchor point of the layer‘s bounds rect, as a point in

* normalized layer coordinates - ‘(0, 0)‘ is the bottom left corner of

* the bounds rect, ‘(1, 1)‘ is the top right corner. Defaults to

* ‘(0.5, 0.5)‘, i.e. the center of the bounds rect. Animatable. */

// 这个就是游戏中必须要懂的锚点,默认为(0.5, 0.5),也就是正中央。

// 关于锚点的知识,当年自学cocos2d-x的时候也困扰过我一段时间,这个有专门的文章讲解的

// 大家可以查一查相关专题讲解。因为这个确实不好理解,一时说不通。

// 支持隐式动画

@property CGPoint anchorPoint;

/* A transform applied to the layer relative to the anchor point of its

* bounds rect. Defaults to the identity transform. Animatable. */

// 图层形变,做动画常用

// 支持隐式动画

@property CATransform3D transform;

温馨提示:在CALayer中很少使用frame属性,因为frame本身不支持动画效果,通常使用boundsposition代替。CALayer中透明度使用opacity表示而不是alpha;中心点使用position表示而不是center

实战点击放大移动效果

先看看效果图:

实现代码逻辑:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

#define kLayerWidth 50

@interface HYBMoveCircleLayerController ()

@property (nonatomic, strong) CALayer *movableCircleLayer;

@end

@implementation HYBMoveCircleLayerController

- (void)viewDidLoad {

[super viewDidLoad];

self.movableCircleLayer = [CALayer layer];

// 指定大小

self.movableCircleLayer.bounds = CGRectMake(0, 0, kLayerWidth, kLayerWidth);

// 指定中心点

self.movableCircleLayer.position = self.view.center;

// 变成圆形

self.movableCircleLayer.cornerRadius = kLayerWidth / 2;

// 指定背景色

self.movableCircleLayer.backgroundColor = [UIColor blueColor].CGColor;

// 设置阴影

self.movableCircleLayer.shadowColor = [UIColor grayColor].CGColor;

self.movableCircleLayer.shadowOffset = CGSizeMake(3, 3);

self.movableCircleLayer.shadowOpacity = 0.8;

[self.view.layer addSublayer:self.movableCircleLayer];

}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

CGFloat width = kLayerWidth;

if (self.movableCircleLayer.bounds.size.width <= kLayerWidth) {

width = kLayerWidth * 2.5;

}

// 修改大小

self.movableCircleLayer.bounds = CGRectMake(0, 0, width, width);

// 将中心位置放到点击位置

self.movableCircleLayer.position = [[touches anyObject] locationInView:self.view];

// 再修改成圆形

self.movableCircleLayer.cornerRadius = width / 2;

}

@end

这里需要注意的是每次更新位置时,这个图层的大小和cornerRadius都需要更新,否则就不成圆形了!

通过层内容呈现图片

效果图片:

代码实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

- (void)drawImageWithContent {

CALayer *layer = [CALayer layer];

layer.bounds = CGRectMake(0, 0, kPhotoWidth, kPhotoWidth);

layer.position = self.view.center;

layer.cornerRadius = kPhotoWidth / 2;

// 要设置此属性才能裁剪成圆形,但是添加此属性后,下面设置的阴影就没有了。

layer.masksToBounds = YES;

layer.borderColor = [UIColor whiteColor].CGColor;

layer.borderWidth = 1;

// 如果只是显示图片,不做其它处理,直接设置contents就可以了,也就不会出现

// 绘图和图像倒立的问题了

layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"bb"].CGImage);

[self.view.layer addSublayer:layer];

}

如果我们只是需要显示图片到图层上,通过设置contents属性就可以了,也就不用绘图,也不会出现图像倒立的问题了。

通过层代理绘制图片



上面的内容呈现是很简单的,但是如果我们要增加其它效果呢?那就需要别的方式了。如下效果图:

代码实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

- (void)drawImage {

CALayer *layer = [CALayer layer];

layer.bounds = CGRectMake(0, 0, kPhotoWidth, kPhotoWidth);

layer.position = self.view.center;

layer.cornerRadius = kPhotoWidth / 2;

// 要设置此属性才能裁剪成圆形,但是添加此属性后,下面设置的阴影就没有了。

layer.masksToBounds = YES;

layer.borderColor = [UIColor whiteColor].CGColor;

layer.borderWidth = 1;

//  // 阴影

//  layer.shadowColor = [UIColor blueColor].CGColor;

//  layer.shadowOffset = CGSizeMake(4, 4);

//  layer.shadowOpacity = 0.9;

// 指定代理

layer.delegate = self;

// 添加到父图层上

[self.view.layer addSublayer:layer];

// 当设置masksToBounds为YES后,要想要阴影效果,就需要额外添加一个图层作为阴影图层了

CALayer *shadowLayer = [CALayer layer];

shadowLayer.position = layer.position;

shadowLayer.bounds = layer.bounds;

shadowLayer.cornerRadius = layer.cornerRadius;

shadowLayer.shadowOpacity = 1.0;

shadowLayer.shadowColor = [UIColor redColor].CGColor;

shadowLayer.shadowOffset = CGSizeMake(2, 1);

shadowLayer.borderWidth = layer.borderWidth;

shadowLayer.borderColor = [UIColor whiteColor].CGColor;

[self.view.layer insertSublayer:shadowLayer below:layer];

// 调用此方法,否则代理不会调用

[layer setNeedsDisplay];

}

#pragma mark - CALayerDelegate

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {

// 将当前上下文入栈

CGContextSaveGState(ctx);

// 注意:坐标系统与UIView的不同,这里使用的是笛卡尔积坐标系,也就是左下角为(0,0)

// 所以,我们只要记住这点就可以很容易地变换了。

// 处理图片倒立的问题

// 默认呈现是倒立的,因此需要将形变矩阵的sy设置为-1就成了正立的了

// 先缩放后平移也可以

// CGContextScaleCTM(ctx, 1, -1);

// CGContextTranslateCTM(ctx, 0, -kPhotoWidth);

// 先向平移后旋转也可以解决倒立的问题

CGContextTranslateCTM(ctx, kPhotoWidth, kPhotoWidth);

CGContextRotateCTM(ctx, 3.1415926 / 180 * 180);

UIImage *image = [UIImage imageNamed:@"bb"];

CGContextDrawImage(ctx, CGRectMake(0, 0, kPhotoWidth, kPhotoWidth), image.CGImage);

// 任务完成后,将当前上下文退栈

CGContextRestoreGState(ctx);

}

我们需要注意,一旦设置了层的layer.masksToBoundsYES,那么阴影效果就没有了,也就是不能直接对该层设置shadow相关的属性了,设置了也没有作用。因此,这里使用另外一个图层来专门呈现阴影效果的。

我们需要将阴影层放在图片层的下面,别把图片层给挡住了。默认是不会调用代理方法的,必须要调用setNeedsDisplay方法才会回调。因此,要绘制哪个层就调用哪个层对象调用该方法。

在代理方法中,我们介绍一下相关代码。CGContextSaveGState方法是入栈操作,也就是将当前设备上下文入栈。既然有入栈,自然也得有出栈,最后一行CGContextRestoreGState就是将设备上下文出栈。我们必须明确一点,绘图是在设备上下文上操作的,因此,我们所进行的绘图操作都会是栈顶元素上的。

矩阵操作:通过CGContextDrawImage绘制的图片是倒立的,因此我们需要进行矩阵相关变换。关于矩阵变换的知识是数学知识,也是知识难点,关于此理论知识,大家可以阅读:http://www.tuicool.com/articles/Er6VNf6

我们需要明确坐标系为笛卡尔积坐标系,坐标原点在左下角。这里提供了两种解决图像倒立问题的方法。

  • 第一种:先绽放后平移
  • 第二种:先平移后旋转

对于第一种,我们先设置矩阵的sx,sy分别为1,-1,然后平移到(0, -kPhotoWidth)。对于这一种不好理解,因此,笔者提供了第二种方法,更容易理解一些。

第二种,先平移到右上角,然后再旋转180度,正好正立。

网络上的一张图片:

坐标原点在左下角,我们将倒立的图片先平移到右上角,再顺时针旋转180度正好形成正立。

时间: 05-01

CALayer精讲的相关文章

第三百六十四节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)的mapping映射管理

第三百六十四节,Python分布式爬虫打造搜索引擎Scrapy精讲-elasticsearch(搜索引擎)的mapping映射管理 1.映射(mapping)介绍 映射:创建索引的时候,可以预先定义字段的类型以及相关属性elasticsearch会根据json源数据的基础类型猜测你想要的字段映射,将输入的数据转换成可搜索的索引项,mapping就是我们自己定义的字段数据类型,同时告诉elasticsearch如何索引数据以及是否可以被搜索 作用:会让索引建立的更加细致和完善 类型:静态映射和动态

面试必看:java面试考点干货精讲视频教程

Java作为目前比较火的计算机语言之一,连续几年蝉联最受程序员欢迎的计算机语言榜首,因此每年新入职Java程序员也数不胜数.很多java程序员在学成之后,会面临着就业的问题.在面试的过程中,面试技巧是一项很重要的能力. 今天要给大家介绍的是一个java经典面试套路精讲视频教程,需要的朋友可以看看,希望能帮助到大家! 课程目录: 第一节. String Stringbuffer Stringbuilder 深度解析第二节. 完美回答面试题Int 与Integer的区别第三节. 以数据结构挖掘集合面

《Linux系统精讲》学习总结(二)

本次总结我将采取总结性,对比性的方式一目了然的展示出来,首先将所有的命令总结在一起,然后对比性的总结部分知识点,最后谈谈本周的学习体会. 一.Linux系统精讲常用命令 格式:命令作用 命令 :命令语法:#注释说明或者补充 例如:创建目录 mkdir:mkdir -m/p 目录名称: 创建目录 mkdir:mkdir -m/p 目录名称: 删除目录 rmdir:rmdir -m/p 目录名称: rm:  rm -r 目录名称= rmdir;  #系统会询问是否删除: rm -f 目录名称:  

iOS开发——语法篇OC篇&amp;高级语法精讲二

Objective高级语法精讲二 Objective-C是基于C语言加入了面向对象特性和消息转发机制的动态语言,这意味着它不仅需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和转发.下面通过分析Apple开源的Runtime代码(我使用的版本是objc4-646.tar)来深入理解Objective-C的Runtime机制. Runtime数据结构 在Objective-C中,使用[receiver message]语法并不会马上执行receiver对象的message方

Oracle数据库精讲之数据库管理

Oracle数据库精讲之数据库管理 课程观看地址:http://www.xuetuwuyou.com/course/165 课程出自学途无忧网:http://www.xuetuwuyou.com 一.课程用到的软件:oracle 11g 二.课程目标: 1. 为有意从事oracle dba工作人员提供学习指导. 2. 提供oracle数据库dba日常管理方法. 3. 掌握oracle数据库体系机构,为oracle优化奠定基础. 三.适合人群: 1.有意从事oracle 数据库工作,担任dba角色

Spark全面精讲视频_Spark2.0视频

Spark全面精讲(基于Spark2版本+含Spark调优+超多案例) 课程观看地址:http://www.xuetuwuyou.com/course/149 课程出自学途无忧网:http://www.xuetuwuyou.com/ 课程分五个阶段,共115课时! 第一阶段 Spark内核深度剖析 第00节课-课程特色和学习方式 第一节课-Spark概述(四大特性) 第二节课-Spark入门 第三节课-什么是RDD? 第四节课-spark架构 第五节课-linux环境准备(虚拟机,linux)

第三百七十一节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现我的搜索以及热门搜索

第三百七十一节,Python分布式爬虫打造搜索引擎Scrapy精讲-elasticsearch(搜索引擎)用Django实现我的搜索以及热门 我的搜素简单实现原理我们可以用js来实现,首先用js获取到输入的搜索词设置一个数组里存放搜素词,判断搜索词在数组里是否存在如果存在删除原来的词,重新将新词放在数组最前面如果不存在直接将新词放在数组最前面即可,然后循环数组显示结果即可 热门搜索实现原理,当用户搜索一个词时,可以保存到数据库,然后记录搜索次数,利用redis缓存搜索次数最到的词,过一段时间更新

第三百七十节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索结果分页

第三百七十节,Python分布式爬虫打造搜索引擎Scrapy精讲-elasticsearch(搜索引擎)用Django实现搜索结果分页 逻辑处理函数 计算搜索耗时 在开始搜索前:start_time = datetime.now()获取当前时间 在搜索结束后:end_time = datetime.now()获取当前时间 last_time = (end_time-start_time).total_seconds()结束时间减去开始时间等于用时,转换成秒 from django.shortcu

第三百六十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索的自动补全功能

第三百六十八节,Python分布式爬虫打造搜索引擎Scrapy精讲-用Django实现搜索的自动补全功能 elasticsearch(搜索引擎)提供了自动补全接口 官方说明:https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-completion.html 创建自动补全字段 自动补全需要用到一个字段名称为suggest类型为Completion类型的一个字段 所以我们需要用