上一篇《iOS CALayer圖層漫談(一)》我們聊了CALayer的基本的概念和有關(guān)寄宿圖的一些屬性,這一篇呢我們來(lái)聊一下CALayer幾何學(xué)相關(guān)的事情饶火。
CALayer的布局
說(shuō)到布局我們知道UIView有三個(gè)特別重要的布局屬性:frame
、bounds
和center
近尚,CALayer同樣也有這樣三個(gè)屬性击碗,只不過(guò)center
在CALayer中叫做position
(為什么叫法不一樣呢丧没?這里是有原因的,后面會(huì)給大家解釋?zhuān)?/p>
平時(shí)當(dāng)我們改變UIView的frame
的時(shí)候沼死,實(shí)際上是在改變視圖中l(wèi)ayer的frame
着逐,也就是說(shuō)我們不能只改變UIView的frame
而讓layer的frame
不變,它們之間是同步的意蛀。至于這三個(gè)屬性的用法這里就不多做解釋了耸别,我想聊一下的是這三個(gè)屬性之間,以及和transform
之間的一些關(guān)系县钥。
對(duì)于視圖或者圖層來(lái)說(shuō)秀姐,frame
其實(shí)是一個(gè)虛擬屬性,它是根據(jù)bounds若贮、position和transform計(jì)算而來(lái)的(你可以通過(guò)下面的圖來(lái)理解)囊扳,所以當(dāng)其中任何一個(gè)值發(fā)生變化,frame都會(huì)變化兜看。反之锥咸,改變frame也會(huì)影響到他們當(dāng)中的值。
通常情況下细移,frame
的寬高和bounds
的寬高是相同的搏予,但是當(dāng)改變了transform
的時(shí)候,frame
的寬高和bounds
的寬高可能就不再相同了弧轧。通過(guò)下圖大家可以理解一下這些屬性之間的關(guān)系:
CALayer的“圖釘說(shuō)” -- 錨點(diǎn)
在聊錨點(diǎn)之前雪侥,我們有必要重新認(rèn)識(shí)一下center
屬性和position
屬性。我們知道精绎,這兩個(gè)屬性都是設(shè)置當(dāng)前視圖(或圖層)的中心點(diǎn)在父視圖(或父圖層)坐標(biāo)系統(tǒng)中位置速缨,從而確定當(dāng)前視圖(或圖層)在父視圖(或父圖層)位置。作為視圖這個(gè)點(diǎn)是當(dāng)前視圖的中心點(diǎn)代乃,但是對(duì)于圖層來(lái)說(shuō)可能就不是中心點(diǎn)了旬牲。
在CALayer中有一個(gè)屬性叫做anchorPoint
仿粹,也就是我們接下來(lái)要說(shuō)的錨點(diǎn)。但是在UIView中原茅,這個(gè)屬性并沒(méi)有被接口暴露出來(lái)吭历,這也是為什么UIView的position
屬性被叫做center
的原因,因?yàn)?code>center包含了位置
+中心
兩層含義擂橘,center
其實(shí)是由position
和anchorPoint
兩個(gè)屬性疊加得到的晌区,此時(shí)anchorPoint
對(duì)應(yīng)的值是默認(rèn)的中心點(diǎn)位置。
那么position
和anchorPoint
到底代表什么呢通贞?下面我們通過(guò)一個(gè)物理模型“圖釘說(shuō)”來(lái)幫助大家理解一下(請(qǐng)牢記這個(gè)模型):
我們可以把當(dāng)前圖層比作一張紙朗若,父圖層比作一面墻。我們通過(guò)
position
和anchorPoint
屬性將當(dāng)前圖層添加到父圖層上昌罩,其實(shí)就相當(dāng)于拿一枚圖釘將這張紙固定到這面墻上捡偏。那么position
和anchorPoint
分別代表這個(gè)物理模型中的什么呢?這里圖釘穿過(guò)紙張的位置就是anchorPoint
錨點(diǎn)的位置峡迷,圖釘扎在墻上的位置就是position
的位置银伟。也就是說(shuō)anchorPoint
錨點(diǎn)的位置是相對(duì)于當(dāng)前圖層的,而position
是相對(duì)于父圖層的绘搞。
【 這里要說(shuō)明一點(diǎn)的是:anchorPoint
使用的坐標(biāo)系統(tǒng)跟我們上篇文章提到的contentsRect
是一樣的彤避,使用的是相對(duì)坐標(biāo)系統(tǒng),也就是說(shuō)左上角的坐標(biāo)是{0,0}右下角的坐標(biāo)是{1,1}夯辖。并且我們也可以通過(guò)設(shè)置小于0或大于1的值琉预,把錨點(diǎn)放置在圖層范圍之外≥锕樱】
看完上面這個(gè)物理模型之后圆米,我們?cè)賮?lái)思考這樣一個(gè)問(wèn)題:如果在position
不變的情況下改變anchorPoint
,此時(shí)會(huì)發(fā)生什么變化啄栓?繼續(xù)利用上面的模型娄帖,相當(dāng)于我們將圖釘摘下,從紙的另一個(gè)位置穿過(guò)昙楚,然后重新扎回上次的位置近速。此時(shí)圖釘在墻上的位置不變,但是紙張相對(duì)墻的位置卻發(fā)生了變化堪旧,其實(shí)也就是frame
發(fā)生了變化削葱。我們可以通過(guò)下圖進(jìn)行進(jìn)一步的理解:
關(guān)于anchorPoint
和position
就先聊這么多,如果想找一個(gè)應(yīng)用場(chǎng)景通過(guò)代碼來(lái)深入理解一下anchorPoint
和position
的話(huà)淳梦,可以去看我的另一篇文章《iOS制作一個(gè)模擬時(shí)鐘》析砸,文中模擬時(shí)鐘的制作就用到了錨點(diǎn)的知識(shí)。這里還要給大家補(bǔ)充另外一個(gè)知識(shí)點(diǎn):當(dāng)一個(gè)圖層通過(guò)transform
進(jìn)行旋轉(zhuǎn)的時(shí)候爆袍,圖層所圍繞的中心就是錨點(diǎn)首繁,這跟我們上面提到的物理模型也是相吻合的作郭。
CALayer的“空間”轉(zhuǎn)換
我們知道不管是UIView還是CALayer,它們的位置定義都是相對(duì)于它們的父視圖(或圖層)的蛮瞄,我們拿到也是它們相對(duì)于父視圖(或圖層)坐標(biāo)系統(tǒng)的位置信息所坯。但是有時(shí)候我們想知道的并不是它們相對(duì)于父視圖(或圖層)坐標(biāo)系統(tǒng)的位置信息谆扎,而是它們的絕對(duì)位置或者相對(duì)于其他視圖(或圖層)的位置怎么辦呢挂捅?
CALayer給我們提供了一些很友好的方法:
- (CGPoint)convertPoint:(CGPoint)p fromLayer:(nullable CALayer *)l;
- (CGPoint)convertPoint:(CGPoint)p toLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r fromLayer:(nullable CALayer *)l;
- (CGRect)convertRect:(CGRect)r toLayer:(nullable CALayer *)l;
我們知道在iOS上坐標(biāo)系的原點(diǎn)通常位于左上角,但是在MacOS上通常是位于左下角堂湖,跟我們通诚邢龋現(xiàn)實(shí)中使用的x/y軸坐標(biāo)系是一樣的。在CALayer中无蜂,它提供了一個(gè)屬性叫做geometryFlipped
伺糠,我們可以通過(guò)這個(gè)屬性對(duì)坐標(biāo)系進(jìn)行翻轉(zhuǎn),當(dāng)設(shè)置成YES后斥季,iOS中训桶,坐標(biāo)原點(diǎn)變?yōu)樵谧笙陆牵籑acOS中酣倾,坐標(biāo)原點(diǎn)變?yōu)樵谧笊辖恰?/p>
和UIView所在的二維坐標(biāo)系統(tǒng)不同舵揭,CALayer是存在于一個(gè)三維空間中的,除了上面說(shuō)過(guò)的position
和anchorPoint
屬性外躁锡,CALayer還有另外兩個(gè)屬性zPosition
和anchorPointZ
午绳,這兩個(gè)浮點(diǎn)型屬性是用來(lái)描述圖層在z軸方向上的位置,而z軸的方向是垂直于屏幕平面指向人眼方向的映之。當(dāng)我們?cè)O(shè)置各圖層的這兩個(gè)屬性的時(shí)候拦焚,由于各圖層在z軸方向上分布的位置不同,就會(huì)產(chǎn)生前后遮擋的位置關(guān)系杠输,從而改變圖層樹(shù)的層級(jí)關(guān)系赎败。雖然這個(gè)兩個(gè)屬性我們平時(shí)很少用到,但是在涉及CATransform3D三維空間移動(dòng)和旋轉(zhuǎn)圖層的時(shí)候就會(huì)經(jīng)常出現(xiàn)蠢甲。
CALayer作為事件響應(yīng)的“輔助英雄”
在上一篇文章開(kāi)始我們就提到螟够,CALayer并不能獨(dú)立的完成事件響應(yīng),它也不能直接處理觸摸事件或者手勢(shì)峡钓。但是作為UIView的“輔助”妓笙,它還是要做一些“輔助英雄”該干的事兒的。
CALayer提供了兩個(gè)方法:-containsPoint:
和-hitTest:
能岩。
-containsPoint:
方法接收一個(gè)在本圖層坐標(biāo)系下的CGPoint
對(duì)象寞宫,如果在圖層范圍內(nèi),就返回YES
拉鹃,這樣我們就可以通過(guò)遍歷視圖樹(shù)(注意這里指的是視圖樹(shù)而不是圖層樹(shù))的方式找出應(yīng)該接收觸摸事件的圖層以及它對(duì)應(yīng)的UIView辈赋。但是這樣做實(shí)在是太麻煩了鲫忍,我們不但要手動(dòng)的遍歷視圖樹(shù),還要將觸摸點(diǎn)逐個(gè)轉(zhuǎn)換成每個(gè)圖層坐標(biāo)系下的點(diǎn)钥屈。為此CALayer給我們提供了另外一個(gè)方法-hitTest:
悟民。
-hitTest:
方法接收的同樣是一個(gè)CGPoint
對(duì)象,但是返回值不是BOOL
類(lèi)型了篷就,而是圖層本身射亏,或者包含這個(gè)觸摸坐標(biāo)點(diǎn)的葉子節(jié)點(diǎn)圖層。也就是說(shuō)-hitTest:
方法會(huì)自動(dòng)幫我們遍歷它跟它的子圖層竭业,并返回包含這個(gè)坐標(biāo)點(diǎn)的圖層智润,如果都不包含則返回nil
。
這里要說(shuō)一下為什么上文中說(shuō)遍歷的是視圖樹(shù)而不是圖層樹(shù)未辆,因?yàn)樵谥傲?code>zPosition屬性的時(shí)候說(shuō)過(guò)窟绷,改變zPosition
屬性值得時(shí)候會(huì)改變圖層樹(shù)的層級(jí)關(guān)系,但是此時(shí)layer
對(duì)應(yīng)的視圖樹(shù)的層級(jí)關(guān)系是不發(fā)生變化的咐柜,所以說(shuō)對(duì)于觸摸手勢(shì)的傳遞順序還是按照視圖樹(shù)的層次順序來(lái)兼蜈,所以這樣就會(huì)導(dǎo)致更改了zPosition
值位置靠前的圖層視圖不響應(yīng)觸摸手勢(shì)的問(wèn)題。
CALayer的自適應(yīng)
在iOS開(kāi)發(fā)中我們經(jīng)常使用自動(dòng)布局相關(guān)的一些API拙友,但是對(duì)于CALayer來(lái)說(shuō)自動(dòng)布局還是相對(duì)較弱的为狸,當(dāng)然這只是在iOS中是這樣,在MacOS中我們可以通過(guò)CALayerManager
協(xié)議和CAConstraintLayoutManager
類(lèi)來(lái)實(shí)現(xiàn)自動(dòng)排版的機(jī)制献宫,而在iOS中如果想隨意控制CALayer的布局钥平,就需要手動(dòng)操作了。最簡(jiǎn)便的方法就是使用CALayerDelegate
中的代理方法:
- (void)layoutSublayersOfLayer:(CALayer *)layer;
當(dāng)圖層的bounds
發(fā)生變化姊途,或者圖層的-setNeedsLayout
方法被調(diào)用涉瘾,這個(gè)代理方法就會(huì)被執(zhí)行,然后我們就可以在代理方法中重新手動(dòng)的擺放調(diào)整子圖層的位置大小了捷兰,但是不能像UIView的autoresizingMask
和constraints
屬性做到自適應(yīng)屏幕旋轉(zhuǎn)立叛。所以在iOS中涉及到自適應(yīng)布局的時(shí)候還是推薦使用UIView暴露出來(lái)的UIViewAutoresizingMask
和NSLayoutConstraint的API
接口。
下一篇《iOS CALayer圖層漫談(三)》我們將聊一聊CALayer視覺(jué)效果相關(guān)的一些事情贡茅。
版權(quán)聲明:出自MajorLMJ技術(shù)博客的原創(chuàng)作品 秘蛇,轉(zhuǎn)載時(shí)必須注明出處及相應(yīng)鏈接!