引言
相信初接觸到CALayer的人都會(huì)遇到以下幾個(gè)問題:
為什么修改anchorPoint會(huì)移動(dòng)layer的位置?
CALayer的position點(diǎn)是哪一點(diǎn)呢?
anchorPoint與position有什么關(guān)系?
我也迷惑過,找過網(wǎng)上的教程,大部分都是復(fù)制粘貼的,有些是翻譯的文章但很有問題,看得似懂非懂,還是自己寫代碼徹底弄懂了,做點(diǎn)筆記吧。
每一個(gè)UIView內(nèi)部都默認(rèn)關(guān)聯(lián)著一個(gè)CALayer, UIView有frame、bounds和center三個(gè)屬性,CALayer也有類似的屬性,分別為frame、bounds、position、anchorPoint。frame和bounds比較好理解,bounds可以視為x坐標(biāo)和y坐標(biāo)都為0的frame,那position、anchorPoint是什么呢?先看看兩者的原型,可知都是CGPoint點(diǎn)。
@property CGPoint position
@property CGPoint anchorPoint
anchorPoint
一般都是先介紹position,再介紹anchorPoint。我這里反過來,先來說說anchorPoint。
從一個(gè)例子開始入手吧,想象一下,把一張A4白紙用圖釘訂在書桌上,如果訂得不是很緊的話,白紙就可以沿順時(shí)針或逆時(shí)針方向圍繞圖釘旋轉(zhuǎn),這時(shí)候圖釘就起著支點(diǎn)的作用。我們要解釋的anchorPoint就相當(dāng)于白紙上的圖釘,它主要的作用就是用來作為變換的支點(diǎn),旋轉(zhuǎn)就是一種變換,類似的還有平移、縮放。
繼續(xù)擴(kuò)展,很明顯,白紙的旋轉(zhuǎn)形態(tài)隨圖釘?shù)奈恢貌煌煌瑘D釘訂在白紙的正中間與左上角時(shí)分別造就了兩種旋轉(zhuǎn)形態(tài),這是由圖釘(anchorPoint)的位置決定的。如何衡量圖釘(anchorPoint)在白紙中的位置呢?在iOS中,anchorPoint點(diǎn)的值是用一種相對bounds的比例值來確定的,在白紙的左上角、右下角,anchorPoint分為為(0,0), (1, 1),也就是說anchorPoint是在單元坐標(biāo)空間(同時(shí)也是左手坐標(biāo)系)中定義的。類似地,可以得出在白紙的中心點(diǎn)、左下角和右上角的anchorPoint為(0.5,0.5), (0,1), (1,0)。
然后再來看下面兩張圖,注意圖中分iOS與MacOS,因?yàn)閮烧叩淖鴺?biāo)系不相同,iOS使用左手坐標(biāo)系,坐標(biāo)原點(diǎn)在左上角,MacOS使用右手坐標(biāo)系,原點(diǎn)在左下角,我們看iOS部分即可。
圖1
圖2
像UIView有superView與subView的概念一樣,CALayer也有superLayer與layer的概念,前面說到的白紙和圖中的矩形可以理解為layer,書桌和圖中矩形以外的坐標(biāo)系可以理解成superLayer。如果各自以左上角為原點(diǎn),則在圖中有相對的兩個(gè)坐標(biāo)空間。
position
在圖1中,anchorPoint有(0.5,0.5)和(0,0)兩種情況,分別為矩形的中心點(diǎn)與原點(diǎn)。那么,這兩個(gè)anchorPoint在superLayer中的實(shí)際位置分別為多少呢?簡單計(jì)算一下就可以得到(100, 100)和(40, 60),把這兩個(gè)值分別與各自的position值比較,發(fā)現(xiàn)完全一致,該不會(huì)是巧合?
這時(shí)候可以大膽猜測一下,position是不是就是anchorPoint在superLayer中的位置呢?答案是確定的,更確切地說,position是layer中的anchorPoint點(diǎn)在superLayer中的位置坐標(biāo)。因此可以說, position點(diǎn)是相對suerLayer的,anchorPoint點(diǎn)是相對layer的,兩者是相對不同的坐標(biāo)空間的一個(gè)重合點(diǎn)。
再來看看position的原始定義: The layer’s position in its superlayer’s coordinate space。
中文可以理解成為position是layer相對superLayer坐標(biāo)空間的位置,很顯然,這里的位置是根據(jù)anchorPoint來確定的。
圖2中是矩形沿不同的anchorPoint點(diǎn)旋轉(zhuǎn)的形態(tài),這就是類似于剛才講的圖釘訂在白紙的正中間與左上角時(shí)分別造就了兩種旋轉(zhuǎn)形態(tài)。
anchorPoint、position、frame
anchorPoint的默認(rèn)值為(0.5,0.5),也就是anchorPoint默認(rèn)在layer的中心點(diǎn)。默認(rèn)情況下,使用addSublayer函數(shù)添加layer時(shí),如果已知layer的frame值,根據(jù)上面的結(jié)論,那么position的值便可以用下面的公式計(jì)算:
position.x = frame.origin.x + 0.5 * bounds.size.width;
position.y = frame.origin.y + 0.5 * bounds.size.height;
里面的0.5是因?yàn)閍nchorPoint取默認(rèn)值,更通用的公式應(yīng)該是下面的:
position.x = frame.origin.x + anchorPoint.x * bounds.size.width;
position.y = frame.origin.y + anchorPoint.y * bounds.size.height;
下面再來看另外兩個(gè)問題,如果單方面修改layer的position位置,會(huì)對anchorPoint有什么影響呢?修改anchorPoint又如何影響position呢?
根據(jù)代碼測試,兩者互不影響,受影響的只會(huì)是frame.origin,也就是layer坐標(biāo)原點(diǎn)相對superLayer會(huì)有所改變。換句話說,frame.origin由position和anchorPoint共同決定,上面的公式可以變換成下面這樣的:
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;
這就解釋了為什么修改anchorPoint會(huì)移動(dòng)layer,因?yàn)閜osition不受影響,只能是frame.origin做相應(yīng)的改變,因而會(huì)移動(dòng)layer。
理解與運(yùn)用
在Apple doc對frame的描述中有這么一句話:
Layers have an implicit frame that is a function of the position, bounds, anchorPoint, and transform properties.
可以看到我們推導(dǎo)的公式基本符合這段描述,只不過還缺少了transform,加上transform的話就比較復(fù)雜,這里就不展開講了。
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.
大意是:當(dāng)你設(shè)置圖層的frame屬性的時(shí)候,position根據(jù)錨點(diǎn)(anchorPoint)的值來確定,而當(dāng)你設(shè)置圖層的position屬性的時(shí)候,bounds會(huì)根據(jù)錨點(diǎn)(anchorPoint)來確定。
這段翻譯的上半句根據(jù)前面的公式容易理解,后半句可能就有點(diǎn)令人迷惑了,當(dāng)修改position時(shí),bounds的width與height會(huì)隨之修改嗎?其實(shí),position是點(diǎn),bounds是矩形,根據(jù)錨點(diǎn)(anchorPoint)來確定的只是它們的位置,而不是內(nèi)部屬性。所以,上面這段英文這么翻譯就容易理解了:
當(dāng)你設(shè)置圖層的frame屬性的時(shí)候,position點(diǎn)的位置(也就是position坐標(biāo))根據(jù)錨點(diǎn)(anchorPoint)的值來確定,而當(dāng)你設(shè)置圖層的position屬性的時(shí)候,bounds的位置(也就是frame的orgin坐標(biāo))會(huì)根據(jù)錨點(diǎn)(anchorPoint)來確定。
在實(shí)際情況中,可能還有這樣一種需求,我需要修改anchorPoint,但又不想要移動(dòng)layer也就是不想修改frame.origin,那么根據(jù)前面的公式,就需要position做相應(yīng)地修改。簡單地推導(dǎo),可以得到下面的公式:
positionNew.x = positionOld.x + (anchorPointNew.x - anchorPointOld.x)? * bounds.size.widthpositionNew.y = positionOld.y + (anchorPointNew.y - anchorPointOld.y)? * bounds.size.height
但是在實(shí)際使用沒必要這么麻煩。修改anchorPoint而不想移動(dòng)layer,在修改anchorPoint后再重新設(shè)置一遍frame就可以達(dá)到目的,這時(shí)position就會(huì)自動(dòng)進(jìn)行相應(yīng)的改變。寫成函數(shù)就是下面這樣的:
-(void)setAnchorPoint:(CGPoint)anchorpointforView:(UIView*)view{CGRectoldFrame=view.frame;
view.layer.anchorPoint=anchorpoint
;view.frame=oldFrame;}
總結(jié)
1、position是layer中的anchorPoint在superLayer中的位置坐標(biāo)。
2、互不影響原則:單獨(dú)修改position與anchorPoint中任何一個(gè)屬性都不影響另一個(gè)屬性。
3、frame、position與anchorPoint有以下關(guān)系:
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;
第2條的互不影響原則還可以這樣理解:position與anchorPoint是處于不同坐標(biāo)空間中的重合點(diǎn),修改重合點(diǎn)在一個(gè)坐標(biāo)空間的位置不影響該重合點(diǎn)在另一個(gè)坐標(biāo)空間中的位置。
后記
關(guān)于修改anchorPoint為什么會(huì)移動(dòng)layer的位置,在剛才回復(fù)finder的評論時(shí)想到了一個(gè)更好的解釋:
還是以桌子與白紙為例,如果固定圖釘在桌上的位置,也就是positon不變,這個(gè)時(shí)候圖釘處在白紙的不同地方就是不同的anchorPoint,相應(yīng)地也就是不同的frame。
另一方面,如果固定圖釘在白紙上的位置(沒訂在桌子上),不管怎么平移白紙,anchorPoint肯定是不變的,但frame肯定是隨之變化的