引言
相信初接觸到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)的值是用一種相對(duì)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部分即可止剖。
像UIView有superView與subView的概念一樣腺阳,CALayer也有superLayer與layer的概念落君,前面說到的白紙和圖中的矩形可以理解為layer,書桌和圖中矩形以外的坐標(biāo)系可以理解成superLayer亭引。如果各自以左上角為原點(diǎn)绎速,則在圖中有相對(duì)的兩個(gè)坐標(biāo)空間。
position
在圖1中焙蚓,anchorPoint有(0.5,0.5)和(0,0)兩種情況纹冤,分別為矩形的中心點(diǎn)與原點(diǎn)。那么购公,這兩個(gè)anchorPoint在superLayer中的實(shí)際位置分別為多少呢萌京?簡(jiǎn)單計(jì)算一下就可以得到(100, 100)和(40, 60),把這兩個(gè)值分別與各自的position值比較君丁,發(fā)現(xiàn)完全一致枫夺,該不會(huì)是巧合?
這時(shí)候可以大膽猜測(cè)一下绘闷,position是不是就是anchorPoint在superLayer中的位置呢橡庞?答案是確定的,更確切地說印蔗,position是layer中的anchorPoint點(diǎn)在superLayer中的位置坐標(biāo)扒最。因此可以說, position點(diǎn)是相對(duì)suerLayer的,anchorPoint點(diǎn)是相對(duì)layer的华嘹,兩者是相對(duì)不同的坐標(biāo)空間的一個(gè)重合點(diǎn)吧趣。
再來看看position的原始定義: The layer’s position in its superlayer’s coordinate space。 中文可以理解成為position是layer相對(duì)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ì)算:
12
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)該是下面的:
12
position.x = frame.origin.x + anchorPoint.x * bounds.size.width床嫌; position.y = frame.origin.y + anchorPoint.y * bounds.size.height跨释;
下面再來看另外兩個(gè)問題,如果單方面修改layer的position位置厌处,會(huì)對(duì)anchorPoint有什么影響呢鳖谈?修改anchorPoint又如何影響position呢? 根據(jù)代碼測(cè)試阔涉,兩者互不影響缆娃,受影響的只會(huì)是frame.origin,也就是layer坐標(biāo)原點(diǎn)相對(duì)superLayer會(huì)有所改變瑰排。換句話說贯要,frame.origin由position和anchorPoint共同決定,上面的公式可以變換成下面這樣的:
12
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對(duì)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)地修改。簡(jiǎn)單地推導(dǎo)餐蔬,可以得到下面的公式:
12
positionNew.x = positionOld.x + (anchorPointNew.x - anchorPointOld.x) * bounds.size.width positionNew.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ù)就是下面這樣的:
12345
- (void) setAnchorPoint:(CGPoint)anchorpoint forView:(UIView *)view{ CGRect oldFrame = view; view.layer.anchorPoint = anchorpoint; view.frame = oldFrame;}
總結(jié)
1、position是layer中的anchorPoint在superLayer中的位置坐標(biāo)词爬。 2秃嗜、互不影響原則:?jiǎn)为?dú)修改position與anchorPoint中任何一個(gè)屬性都不影響另一個(gè)屬性。 3顿膨、frame锅锨、position與anchorPoint有以下關(guān)系:
12
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)空間中的位置。