前言
相信很多剛接觸CALayer的人都會(huì)遇到一下幾個(gè)麻煩:
1. 為什么修改anchorPoint會(huì)移動(dòng)layer的位置烘豹?
2. CALayer的position點(diǎn)是哪一點(diǎn)呢?
3. anchorPoint與position有什么關(guān)系较锡?
今天我們就來(lái)討論一下這些問(wèn)題
關(guā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
從一個(gè)例子開(kāi)始入手吧侨拦,想象一下殊橙,把一張A4白紙用圖釘訂在書(shū)桌上,如果訂得不是很緊的話狱从,白紙就可以沿順時(shí)針或逆時(shí)針?lè)较驀@圖釘旋轉(zhuǎn)膨蛮,這時(shí)候圖釘就起著支點(diǎn)的作用。我們要解釋的anchorPoint就相當(dāng)于白紙上的圖釘季研,它主要的作用就是用來(lái)作為變換的支點(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的比例值來(lái)確定的桥胞,在白紙的左上角恳守、右下角,anchorPoint分為為(0,0), (1, 1)贩虾,也就是說(shuō)anchorPoint是在單元坐標(biāo)空間(同時(shí)也是左手坐標(biāo)系)中定義的催烘。類似地,可以得出在白紙的中心點(diǎn)缎罢、左下角和右上角的anchorPoint為(0.5,0.5), (0,1), (1,0)颗圣。
然后再來(lái)看下面兩張圖喳钟,注意圖中分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的概念,前面說(shuō)到的白紙和圖中的矩形可以理解為layer范抓,書(shū)桌和圖中矩形以外的坐標(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中的位置呢?答案是確定的兄一,更確切地說(shuō)厘线,position是layer中的anchorPoint點(diǎn)在superLayer中的位置坐標(biāo)。因此可以說(shuō), position點(diǎn)是相對(duì)suerLayer的瘾腰,anchorPoint點(diǎn)是相對(duì)layer的皆的,兩者是相對(duì)不同的坐標(biāo)空間的一個(gè)重合點(diǎn)。
再來(lái)看看position的原始定義: The layer’s position in its superlayer’s coordinate space蹋盆。
中文可以理解成為position是layer相對(duì)superLayer坐標(biāo)空間的位置费薄,很顯然,這里的位置是根據(jù)anchorPoint來(lái)確定的栖雾。
圖2中是矩形沿不同的anchorPoint點(diǎn)旋轉(zhuǎn)的形態(tài)楞抡,這就是類似于剛才講的圖釘訂在白紙的正中間與左上角時(shí)分別造就了兩種旋轉(zhuǎn)形態(tài)。
anchorPoint析藕、position召廷、frame之間的關(guān)系
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;
下面再來(lái)看另外兩個(gè)問(wèn)題劫扒,如果單方面修改layer的position位置檬洞,會(huì)對(duì)anchorPoint有什么影響呢?修改anchorPoint又如何影響position呢沟饥?
根據(jù)代碼測(cè)試添怔,兩者互不影響,受影響的只會(huì)是frame.origin闷板,也就是layer坐標(biāo)原點(diǎn)相對(duì)superLayer會(huì)有所改變澎灸。換句話說(shuō),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īng)用
在Apple doc對(duì)frame的描述中有這么一句話:
Layers have an implicit frame that is a function of the position, bounds, anchorPoint, and transform properties.
可以看到我們推導(dǎo)的公式基本符合這段描述其兴,只不過(guò)還缺少了transform,加上transform的話就比較復(fù)雜夸政,這里就不展開(kāi)講了元旬。
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)的值來(lái)確定守问,而當(dāng)你設(shè)置圖層的position屬性的時(shí)候匀归,bounds會(huì)根據(jù)錨點(diǎn)(anchorPoint)來(lái)確定。
這段翻譯的上半句根據(jù)前面的公式容易理解耗帕,后半句可能就有點(diǎn)令人迷惑了穆端,當(dāng)修改position時(shí),bounds的width與height會(huì)隨之修改嗎仿便?其實(shí),position是點(diǎn)体啰,bounds是矩形攒巍,根據(jù)錨點(diǎn)(anchorPoint)來(lái)確定的只是它們的位置,而不是內(nèi)部屬性荒勇。所以柒莉,上面這段英文這么翻譯就容易理解了:
當(dāng)你設(shè)置圖層的frame屬性的時(shí)候,position點(diǎn)的位置(也就是position坐標(biāo))根據(jù)錨點(diǎn)(anchorPoint)的值來(lái)確定枕屉,而當(dāng)你設(shè)置圖層的position屬性的時(shí)候常柄,bounds的位置(也就是frame的orgin坐標(biāo))會(huì)根據(jù)錨點(diǎn)(anchorPoint)來(lái)確定。
在實(shí)際情況中搀擂,可能還有這樣一種需求,我需要修改anchorPoint卷玉,但又不想要移動(dòng)layer也就是不想修改frame.origin哨颂,那么根據(jù)前面的公式,就需要position做相應(yīng)地修改相种。簡(jiǎn)單地推導(dǎo)威恼,可以得到下面的公式:
positionNew.x = positionOld.x + (anchorPointNew.x - anchorPointOld.x) * bounds.size.width
positionNew.y = positionOld.y + (anchorPointNew.y - anchorPointOld.y) * bounds.size.height
但是在實(shí)際使用沒(méi)必要這么麻煩。修改anchorPoint而不想移動(dòng)layer寝并,在修改anchorPoint后再重新設(shè)置一遍frame就可以達(dá)到目的箫措,這時(shí)position就會(huì)自動(dòng)進(jìn)行相應(yīng)的改變。寫(xiě)成函數(shù)就是下面這樣的:
- (void) setAnchorPoint:(CGPoint)anchorpoint forView:(UIView *)view{
CGRect oldFrame = view.frame;
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)系:
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)空間中的位置漂羊。