CALayer

CALayer属于QuartzCore框架,用于在iOS和Mac OS系统上可见元素的绘制,和属于UIKit框架的UIView的关系是,UIView默认会创建一个CALayer属性,用于图象的绘制和显示.当然,CALayer也可以单独创建.

区别

UIView可以处理用户交互事件,而CALayer不行.
CALayer具备以下UIView没有的功能:

  • 阴影, 圆角, 边框
  • 3D变换
  • 非矩形范围
  • 透明遮罩
  • 多级非线性动画

布局

要分析CALayeranchorPointposition属性,首先要讨论一下CALayer的布局.
我们所熟悉的UIView有三个重要的布局属性:frame,boundscenter,CALayer对应的叫做 frame,boundsposition.

  • frame代表了图层的外部坐标(在父图层上占据的空间)
  • bounds为内部坐标
  • position代表了相对父图层anchorPoint的位置

锚点

如何理解anchorPoint (锚点)?

  • position共同决定图层相对父图层的位置,即framex,y
  • 在图层旋转时的固定点

锚点使用单位坐标来描述,范围为左上角{0, 0}到右下角{1, 1},默认坐标是{0.5, 0.5}.

锚点和position的关系

position是图层的anchorPoint在父图层中的位置坐标.
anchorPointposition共同决定图层相对父图层的位置,即frame属性的frame.origin.
单方面修改anchorPoint或者position并不会对彼此产生影响,修改其中一个值,受影响的只会是frame.origin.
苹果官方文档有如下描述

Layers have an implicit frame that is a function of the position, bounds, anchorPoint, and transform properties.

以防transform的引入混淆大家对positionanchorPoint的理解,我们先不讨论图层旋转的问题.
根据上述文档我们知道,frame属性是通过positionanchorPoint共同决定的,那么所说的function又是什么呢?根据一系统推导可以得出

frame.origin.x = position.x - anchorPoint.x * bounds.size.width;frame.origin.y = position.y - anchorPoint.y * bounds.size.height

官方文档还有另一段描述

  • When you specify the frame of a layer, position is set relative to the anchor point. When you specify the position of the layer, bounds is set relative to the anchor point.

可以理解为:

  • 当你设置图层的frame属性的时候,position点的位置(也就是position坐标)根据锚点(anchorPoint)的值来确定,而当你设置图层的position属性的时候,bounds的位置(也就是frame的orgin坐标)会根据锚点(anchorPoint)来确定.

图层旋转时,如何修改锚点

在图层旋转时,锚点就是图层的固定点,旋转是沿着这个定点进行的.
来根据一个钟表的demo进行说明吧.

表盘布局:

CGFloat clockWidht = 380;
UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(20, 100, clockWidht, clockWidht)];
[self.view addSubview:backgroundView];
backgroundView.backgroundColor = [UIColor lightGrayColor];UIImage *image = [UIImage imageNamed:@"clock"];
backgroundView.layer.contents = (__bridge id)image.CGImage;

表盘

秒针布局

CALayer *secondLayer = [CALayer layer];
secondLayer.frame = CGRectMake(clockWidht / 2 - 20, 70, 40, 120);
secondLayer.contentsGravity = kCAGravityResizeAspect;
secondLayer.contents = (__bridge id)[UIImage imageNamed:@"second"].CGImage;
[backgroundView.layer addSublayer:secondLayer];self.secondLayer = secondLayer;

秒针布局

以上代码只设置了秒针的frame属性,当前默认的锚点是秒针图片的中心位置,{0.5, 0.5},如图

锚点位置

所以如果不改变锚点,设置一个定时器:

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:YES];[self tick];- (void)tick {NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];NSUInteger units = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0;self.secondLayer.transform = CATransform3DMakeRotation(secsAngle, 0, 0, 1);
}

得到的效果如图

锚点{0.5, 0.5}旋转

如果想要使得秒针沿着底部旋转,应该改变锚点:
secondLayer.anchorPoint = CGPointMake(0.5, 1);
发生了什么变化呢?

只修改锚点

由图片可以看到,图层的frame.origin发生了变化,为什么呢?我们回顾上面的内容

frame.origin.x = position.x - anchorPoint.x * bounds.size.width;frame.origin.y = position.y - anchorPoint.y * bounds.size.height

anchorPointposition共同决定了frame,所以需要重新改变图层的frame:
secondLayer.position = CGPointMake(clockWidht / 2, clockWidht / 2);
再次运行,效果正常!!

总结

  • 单方面修改anchorPoint或者position并不会对彼此产生影响,修改其中一个值,受影响的只会是frame.origin.

  • anchorPointposition共同决定了frame
    frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
    frame.origin.y = position.y - anchorPoint.y * bounds.size.height

  • anchorPoint是图层在旋转时的固定点

相关引用

作者:sea_biscute
链接:https://www.jianshu.com/p/998a6119a275
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

CGFloat clockWidht = 300;

UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(20, 100, clockWidht, clockWidht)];

[self.view addSubview:backgroundView];

backgroundView.backgroundColor = [UIColor lightGrayColor];

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

backgroundView.layer.contents = (__bridge id)image.CGImage;

CALayer *secondLayer = [CALayer layer];

secondLayer.frame = CGRectMake(clockWidht / 2 - 20, 70, 40, 80);

secondLayer.contentsGravity = kCAGravityResizeAspect;

secondLayer.contents = (__bridge id)[UIImage imageNamed:@"second"].CGImage;

[backgroundView.layer addSublayer:secondLayer];

// secondLayer.backgroundColor = [[UIColor orangeColor] CGColor];

self.secondLayer = secondLayer;

secondLayer.anchorPoint = CGPointMake(0.5, 1);

secondLayer.position = CGPointMake(secondLayer.position.x, secondLayer.position.y + 40);

彻底理解position与anchorPoint

引言

相信初接触到CALayer的人都会遇到以下几个问题: 
为什么修改anchorPoint会移动layer的位置?
CALayer的position点是哪一点呢?
anchorPoint与position有什么关系?

我也迷惑过,找过网上的教程,大部分都是复制粘贴的,有些是翻译的文章但很有问题,看得似懂非懂,还是自己写代码彻底弄懂了,做点笔记吧。

每一个UIView内部都默认关联着一个CALayer, UIView有frame、bounds和center三个属性,CALayer也有类似的属性,分别为frame、bounds、position、anchorPoint。frame和bounds比较好理解,bounds可以视为x坐标和y坐标都为0的frame,那position、anchorPoint是什么呢?先看看两者的原型,可知都是CGPoint点。

@property CGPoint position
@property CGPoint anchorPoint

anchorPoint

一般都是先介绍position,再介绍anchorPoint。我这里反过来,先来说说anchorPoint。

从一个例子开始入手吧,想象一下,把一张A4白纸用图钉订在书桌上,如果订得不是很紧的话,白纸就可以沿顺时针或逆时针方向围绕图钉旋转,这时候图钉就起着支点的作用。我们要解释的anchorPoint就相当于白纸上的图钉,它主要的作用就是用来作为变换的支点,旋转就是一种变换,类似的还有平移、缩放。

继续扩展,很明显,白纸的旋转形态随图钉的位置不同而不同,图钉订在白纸的正中间与左上角时分别造就了两种旋转形态,这是由图钉(anchorPoint)的位置决定的。如何衡量图钉(anchorPoint)在白纸中的位置呢?在iOS中,anchorPoint点的值是用一种相对bounds的比例值来确定的,在白纸的左上角、右下角,anchorPoint分为为(0,0), (1, 1),也就是说anchorPoint是在单元坐标空间(同时也是左手坐标系)中定义的。类似地,可以得出在白纸的中心点、左下角和右上角的anchorPoint为(0.5,0.5), (0,1), (1,0)。

然后再来看下面两张图,注意图中分iOS与MacOS,因为两者的坐标系不相同,iOS使用左手坐标系,坐标原点在左上角,MacOS使用右手坐标系,原点在左下角,我们看iOS部分即可。

图1

图2

像UIView有superView与subView的概念一样,CALayer也有superLayer与layer的概念,前面说到的白纸和图中的矩形可以理解为layer,书桌和图中矩形以外的坐标系可以理解成superLayer。如果各自以左上角为原点,则在图中有相对的两个坐标空间。

position

在图1中,anchorPoint有(0.5,0.5)和(0,0)两种情况,分别为矩形的中心点与原点。那么,这两个anchorPoint在superLayer中的实际位置分别为多少呢?简单计算一下就可以得到(100, 100)和(40, 60),把这两个值分别与各自的position值比较,发现完全一致,该不会是巧合?

这时候可以大胆猜测一下,position是不是就是anchorPoint在superLayer中的位置呢?答案是确定的,更确切地说,position是layer中的anchorPoint点在superLayer中的位置坐标。因此可以说, position点是相对suerLayer的,anchorPoint点是相对layer的,两者是相对不同的坐标空间的一个重合点。

再来看看position的原始定义: The layer’s position in its superlayer’s coordinate space。
中文可以理解成为position是layer相对superLayer坐标空间的位置,很显然,这里的位置是根据anchorPoint来确定的。

图2中是矩形沿不同的anchorPoint点旋转的形态,这就是类似于刚才讲的图钉订在白纸的正中间与左上角时分别造就了两种旋转形态。

anchorPoint、position、frame

anchorPoint的默认值为(0.5,0.5),也就是anchorPoint默认在layer的中心点。默认情况下,使用addSublayer函数添加layer时,如果已知layer的frame值,根据上面的结论,那么position的值便可以用下面的公式计算:

1
2
position.x = frame.origin.x + 0.5 * bounds.size.width;
position.y = frame.origin.y + 0.5 * bounds.size.height;  

里面的0.5是因为anchorPoint取默认值,更通用的公式应该是下面的:

1
2
position.x = frame.origin.x + anchorPoint.x * bounds.size.width;
position.y = frame.origin.y + anchorPoint.y * bounds.size.height;

下面再来看另外两个问题,如果单方面修改layer的position位置,会对anchorPoint有什么影响呢?修改anchorPoint又如何影响position呢? 
根据代码测试,两者互不影响,受影响的只会是frame.origin,也就是layer坐标原点相对superLayer会有所改变。换句话说,frame.origin由position和anchorPoint共同决定,上面的公式可以变换成下面这样的:

1
2
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;

这就解释了为什么修改anchorPoint会移动layer,因为position不受影响,只能是frame.origin做相应的改变,因而会移动layer。

理解与运用

在Apple doc对frame的描述中有这么一句话:

Layers have an implicit frame that is a function of the position, bounds, anchorPoint, and transform properties.

可以看到我们推导的公式基本符合这段描述,只不过还缺少了transform,加上transform的话就比较复杂,这里就不展开讲了。


Apple doc中还有一句描述是这样的:

When you specify the frame of a layer, position is set relative to the anchor point. When you specify the position of the layer, bounds is set relative to the anchor point.

大意是:当你设置图层的frame属性的时候,position根据锚点(anchorPoint)的值来确定,而当你设置图层的position属性的时候,bounds会根据锚点(anchorPoint)来确定。

这段翻译的上半句根据前面的公式容易理解,后半句可能就有点令人迷惑了,当修改position时,bounds的width与height会随之修改吗?其实,position是点,bounds是矩形,根据锚点(anchorPoint)来确定的只是它们的位置,而不是内部属性。所以,上面这段英文这么翻译就容易理解了:

当你设置图层的frame属性的时候,position点的位置(也就是position坐标)根据锚点(anchorPoint)的值来确定,而当你设置图层的position属性的时候,bounds的位置(也就是frame的orgin坐标)会根据锚点(anchorPoint)来确定。

在实际情况中,可能还有这样一种需求,我需要修改anchorPoint,但又不想要移动layer也就是不想修改frame.origin,那么根据前面的公式,就需要position做相应地修改。简单地推导,可以得到下面的公式:

1
2
positionNew.x = positionOld.x + (anchorPointNew.x - anchorPointOld.x)  * bounds.size.width
positionNew.y = positionOld.y + (anchorPointNew.y - anchorPointOld.y)  * bounds.size.height

但是在实际使用没必要这么麻烦。修改anchorPoint而不想移动layer,在修改anchorPoint后再重新设置一遍frame就可以达到目的,这时position就会自动进行相应的改变。写成函数就是下面这样的:

1
2
3
4
5
- (void) setAnchorPoint:(CGPoint)anchorpoint forView:(UIView *)view{CGRect oldFrame = view;view.layer.anchorPoint = anchorpoint;view.frame = oldFrame;
}

总结

1、position是layer中的anchorPoint在superLayer中的位置坐标。
2、互不影响原则:单独修改position与anchorPoint中任何一个属性都不影响另一个属性。
3、frame、position与anchorPoint有以下关系:

1
2
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;

第2条的互不影响原则还可以这样理解:position与anchorPoint是处于不同坐标空间中的重合点,修改重合点在一个坐标空间的位置不影响该重合点在另一个坐标空间中的位置。

https://www.cnblogs.com/benbenzhu/p/3615516.html

详解CALayer的anchorPoint和position相关推荐

  1. iOS开发CAAnimation详解

    Core Animation,即为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍.也就是说,使用少量的代码就可以实现非常强大的功能.Core Anima ...

  2. ViewPager详解

    ViewPager详解 ViewPager中的主要方法详解 OnPageChangeListener中的三个方法详解 三种适配器的使用及其主要方法详解 ViewPager用于实现页面间的切换. Vie ...

  3. iOS CAAnimation 详解

    CAAnimation 是一个抽像类.(所谓的抽象类,个人理解就是只是定义其子类的公共行为,抽出来写成父类.举例 动物类  和它的子类 老虎,狮子,...难道你想实例化一个小动物 你去示例化 动物类吗 ...

  4. iOS图层配合核心动画详解

    Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍.也就是说,使用少量的代码就可以实现 ...

  5. Kooboo CMS - Html.FrontHtml[Helper.cs] 各个方法详解

    下面罗列了方法详解,每一个方法一篇文章. Kooboo CMS - @Html.FrontHtml().HtmlTitle() 详解 Kooboo CMS - Html.FrontHtml.Posit ...

  6. 【好程序员笔记分享】——UIView与CALayer详解

    -iOS培训,iOS学习-------型技术博客.期待与您交流!------------ UIView与CALayer详解 研究Core Animation已经有段时间了,关于Core Animati ...

  7. Kooboo CMS - Html.FrontHtml.Position 详解

    DataContract 数据契约 http://www.cnblogs.com/Gavinzhao/archive/2010/06/01/1748736.html https://msdn.micr ...

  8. CSS中position详解与常见应用实现

    在web前台开发时候,我们必不可少的会用到postion属性进行布局定位. 今天总结了一下position知识点,与常用功能与大家分享,欢迎大家交流指正. 首先我们对postion属性进行详解. 在C ...

  9. HTML中元素的position属性详解

    HTML中元素的position属性详解 转载自:https://blog.csdn.net/wangzunkuan/article/details/81540935 HTML中DOM元素有5种定位属 ...

最新文章

  1. Jenkins构建Docker容器
  2. TypeScript学习笔记1:变量赋值及书写方式
  3. (原创)C++11改进我们的程序之右值引用
  4. TensorFlow(3)-与训练相关的操作
  5. 动态规划_leetcode322
  6. [剑指Offer] 42.和为S的两个数字
  7. 51nod-1350:斐波那契表示
  8. 验证码类库CaptchaMvc
  9. 测试工具|(本来不想分享的)安卓系统Ping IP工具
  10. 【MATLAB】— xlsread和xlswrite函数
  11. 国际版抖音Tik Tok怎么引流变现,卖家所不知道的那些事
  12. bootstrap 检验 法 原理_Stata:刀切法/留一法/Jackknife 简介
  13. 啥是jQuery(什么是jQuery)
  14. HarmonyOS内存占用,华为HarmonyOS对比EMUI11:内存占用更少 系统更流畅
  15. 大连海洋大学计算机考研资料汇总
  16. 亚马逊云科技 + 英特尔 + 中科创达为行业客户构建 AIoT 平台
  17. powerpovit oracle,在 SharePoint Server 2013 PowerPivot Oracle 数据源配置服务时出错
  18. 【转载】种子搜索神器使用图文教程
  19. 在线编辑文档—字体字号颜色设置
  20. 英语口语255之每日十句口语

热门文章

  1. 介绍各种压缩格式MPEG1--MPEG4--MPEG7—MPEG21-H.264
  2. django项目实战基于Python实现的衣物捐赠系统
  3. iphone游戏时不弹通知_收到通知时如何使iPhone的LED闪烁
  4. ubuntu 12.04 安装惠普打印机
  5. 暑期实习以及实习与秋招浅谈
  6. 专访蒋宇捷:技术管理者应具备哪些能力?(转载)
  7. Lenovo X240/250锁定 Fn 键时 End 键工作不正常
  8. 电影 看不见的客人+生吃
  9. 如何禁用Microsoft Edge的InPrivate功能
  10. 什么是老域名(怎么查老域名历史)