一般開發(fā)中常見的是UIView或者它的子類,CALayer似乎并太不常見,最多也就是在倒圓角或者加陰影或者邊框的時(shí)候想起它來,但是當(dāng)有一些復(fù)雜動(dòng)畫或者不尋常視圖顯示需求的時(shí)候,亦或是界面卡頓束手無策的時(shí)候,你會(huì)發(fā)現(xiàn)CALayer才是UIView背后視圖顯示處理的真正玩家。
iOS開發(fā)常用的視圖控件都是UIView的子類,比如:
UILabel -> UIView
UIImageView -> UIView
UITextView -> UIScrollView -> UIView
UIButton -> UIControl -> UIView
UITextField -> UIControl -> UIView
UISegmentedControl -> UIControl -> UIView
UIView可以處理觸摸事件,可以支持基于Core Graphics繪圖,可以做仿射變換(如旋轉(zhuǎn)或者縮放),或者簡(jiǎn)單的動(dòng)畫像滑動(dòng)或者漸變。
UIView是基于CALayer的封裝,目的在于接收觸控事件,除此之外像剛提到的簡(jiǎn)單的動(dòng)畫或者仿射變換其實(shí)都是CALayer的能力,UIView只不過是通過對(duì)它的Layer層賦值和取值來完成這一操作的,每一個(gè)UIView都有一個(gè)CALayer實(shí)例圖層屬性,這個(gè)屬性會(huì)和之后添加視圖的實(shí)例圖層屬性行成關(guān)系樹,用于控制顯示圖層。
此處也可以留意到,其實(shí)UIView和CALayer是各司其職的,UIView負(fù)責(zé)響應(yīng)鏈的處理,CALayer主要負(fù)責(zé)視圖顯示的圖層關(guān)系和效果的處理,理解這一點(diǎn),在有一些特殊視圖顯示和響應(yīng)鏈操作需求的時(shí)候會(huì)有幫助。蘋果之所以沒有把觸控事件和圖層處理邏輯集中到一個(gè)類處理是因?yàn)閕OS和Mac OS兩個(gè)平臺(tái)的交互事件不一樣,如果直接設(shè)計(jì)兩套代碼,那關(guān)于圖層部分其實(shí)很多是重復(fù)的,大部分區(qū)別只在于觸控事件不同,所以這樣代碼封裝有利于兩個(gè)平臺(tái)共享代碼。
- 布局相關(guān)的位置和尺寸的映射
UIView有三個(gè)比較重要的布局屬性:frame、bounds和center。
CALayer對(duì)應(yīng)的是:frame、bounds和position。
我們?cè)诓僮?code>UIView這三個(gè)屬性的時(shí)候?qū)嶋H上是操作CALayer對(duì)應(yīng)的三個(gè)屬性,所以UIView的三個(gè)屬性僅僅是存取方法。所以給CALayer賦能“接收”觸控事件是有性能代價(jià)的,由此也可知,如果我們?cè)跊]有事件響應(yīng)需求的時(shí)候,是可以通過直接創(chuàng)建并操作CALayer級(jí)別的圖層顯示來提升性能的。
frame屬性并不是一個(gè)直接屬性或者不是一個(gè)真實(shí)屬性,它是由bounds、position和transform計(jì)算而來的,所以這三個(gè)屬性任一個(gè)改變都會(huì)影響frame,同樣frame改變也會(huì)影響它們。
我們熟知的是frame是相對(duì)于父視圖坐標(biāo)尺寸,bounds是相對(duì)于自己的坐標(biāo)尺寸。這樣我們會(huì)覺得它們可能只是相對(duì)位置坐標(biāo)不一樣,寬高尺寸是一樣的,其實(shí)不然。frame實(shí)際上代表了能覆蓋圖層的整個(gè)軸對(duì)齊的矩形區(qū)域,如果視圖旋轉(zhuǎn)的話,那么frame的寬高和bounds就不一樣了。理解起來有點(diǎn)費(fèi)勁,來張圖就明白了。

-
錨點(diǎn)-anchorPoint
anchorPoint是CALayer的屬性,并沒有對(duì)UIView暴露出來,修改它可以修改圖層的位置,但不改變position。它的取值控件是{0,0}--{1,1}。
坐標(biāo)系
和UIView身處的二維空間不同,CALayer身處于一個(gè)三維空間中,所以它多了一個(gè)垂直于屏幕的z坐標(biāo)軸,相關(guān)的兩個(gè)屬性為CGFloat類型的zPosition和anchorPointZ,一個(gè)用于描述圖層在z坐標(biāo)軸的位置,一個(gè)用于描述在z坐標(biāo)軸方向發(fā)生幾何變化時(shí)的參照錨點(diǎn),默認(rèn)值都是0,也就是所有視圖添加后都在一個(gè)平面內(nèi)。-
技術(shù)小應(yīng)用
這里我們可以假設(shè)一個(gè)需求:有兩個(gè)Button,ButtonA和ButtonB,它們有一部分相交,需求是ButtonA要看起來是在上邊顯示的(如圖),但是點(diǎn)擊到相交的區(qū)域要ButtonB來響應(yīng)。
PlanA:可以從事件響應(yīng)入手,首先判斷點(diǎn)擊的點(diǎn)的區(qū)域是相交的區(qū)域,然后攔截父視圖的- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;方法返回ButtonB對(duì)象,這樣就可以完成需求效果,但是兩步都比較麻煩。
PlanB:結(jié)合前邊的內(nèi)容,我們知道響應(yīng)鏈和UIView有關(guān)系,顯示層級(jí)和CALayer有關(guān)系,現(xiàn)在我們首先要保證響應(yīng)關(guān)系,重疊區(qū)域的事件響應(yīng)是按棧管理來處理的,最后添加的最先響應(yīng),那么我們首先保證ButtonB是最后添加的,此時(shí)ButtonB在重疊區(qū)域是在上邊顯示的,那么我要做的就是把ButtonA的視圖層移動(dòng)到上邊來顯示就可以了,這個(gè)時(shí)候就用到了CALayer的zPosition屬性了,因?yàn)槟J(rèn)都是0,所以把ButtonA視圖的CALayer實(shí)例屬性的zPosition屬性設(shè)置成0.001就OK了,相比PlanA即優(yōu)雅又簡(jiǎn)單。
基于CATextLayer封裝一個(gè)控件,在不需要接收觸控事件的時(shí)候可以替代UILabel。
demo地址
喜歡就點(diǎn)個(gè)贊唄!
歡迎大家提出更好的改進(jìn)意見和建議,一起進(jìn)步!

