彻底理解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部分就可以。 test 
图1

test 
图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)来确定。

这段翻译的上半句依据前面的公式easy理解,后半句可能就有点令人迷惑了,当改动position时,bounds的width与height会随之改动吗?事实上,position是点,bounds是矩形。依据锚点(anchorPoint)来确定的仅仅是它们的位置。而不是内部属性。

所以,上面这段英文这么翻译就easy理解了:

当你设置图层的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.frame;
  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是处于不同坐标空间中的重合点,改动重合点在一个坐标空间的位置不影响该重合点在还有一个坐标空间中的位置。

后记

20140323:关于改动anchorPoint为什么会移动layer的位置,在刚才回复finder的评论时想到了一个更好的解释: 
还是以桌子与白纸为例。假设固定图钉在桌上的位置。也就是positon不变。这个时候图钉处在白纸的不同地方就是不同的anchorPoint,对应地也就是不同的frame。 
还有一方面。假设固定图钉在白纸上的位置(没订在桌子上)。无论怎么平移白纸。anchorPoint肯定是不变的,但frame肯定是随之变化的

转载:http://wonderffee.github.io/blog/2013/10/13/understand-anchorpoint-and-position/

原文地址:https://www.cnblogs.com/cxchanpin/p/7371362.html