轉(zhuǎn)https://blog.csdn.net/lxl_815520/article/details/51878882
IOS進(jìn)階 - CALayer簡單介紹
2016年07月11日 13:26:30?閱讀數(shù):1550
* 在iOS系統(tǒng)中蒜焊,你能看得見摸得著的東西基本上都是UIView拭卿,比如一個按鈕胀溺、一個文本標(biāo)簽蜻牢、一個文本輸入框褐鸥、一個圖標(biāo)等等酬荞,這些都是UIView刻像。
? ? ?* 其實UIView之所以能顯示在屏幕上锹安,完全是因為它內(nèi)部的一個層短荐。
? ? ?* 在創(chuàng)建UIView對象時,UIView內(nèi)部會自動創(chuàng)建一個層(即CALayer對象)叹哭,通過UIView的layer屬性可以訪問這個層忍宋。當(dāng)UIView需要顯示到屏幕上時,會調(diào)用 ? ? ? ? ? ? ? ? ?drawRect:方法進(jìn)行繪圖话速,并且會將所有內(nèi)容繪制在自己的層上讶踪,繪圖完畢后,系統(tǒng)會將層拷貝到屏幕上泊交,于是就完成了UIView的顯示乳讥。
? ? ?* 換句話說,UIView本身不具備顯示的功能廓俭,是它內(nèi)部的層才有顯示功能云石。
二、UIView與CALayer的區(qū)別和聯(lián)系
*?UIView是iOS系統(tǒng)中界面元素的基礎(chǔ)研乒,所有的界面元素都繼承自它汹忠。它本身完全是由CoreAnimation來實現(xiàn)的(Mac下似乎不是這樣)。它真正的繪圖部分雹熬,是由一個叫CALayer(Core Animation Layer)的類來管理宽菜。UIView本身,更像是一個CALayer的管理器竿报,訪問它的跟繪圖和跟坐標(biāo)有關(guān)的屬性铅乡,例如frame,bounds等等烈菌,實際上內(nèi)部都是 ? ?在訪問它所包含的CALayer的相關(guān)屬性阵幸。
就是說我們在操作UIView的一些跟繪圖和坐標(biāo)有關(guān)的屬性的時候,比如, self.view.backGround =[UIColor yellowColor] ,本質(zhì)仍然是對CLayer做了操作. 由于代碼封裝我們看不到罷了.
*?UIView有個layer屬性花履,可以返回它的主CALayer實例,UIView有一個layerClass方法挚赊,返回主layer所使用的類诡壁,UIView的子類,可以通過重載這個方法荠割,來讓UIView使用不同的CALayer來顯示妹卿,例如通過
使某個UIView的子類使用GL來進(jìn)行繪制。
*?UIView的CALayer類似UIView的子View樹形結(jié)構(gòu)涨共,也可以向它的layer上添加子layer纽帖,來完成某些特殊的表示。例如下面的代碼
會在目標(biāo)View上敷上一層黑色的透明薄膜举反。
*?UIView的layer樹形在系統(tǒng)內(nèi)部懊直,被系統(tǒng)維護(hù)著三份copy(這段理解有點吃不準(zhǔn))。
第一份火鼻,邏輯樹室囊,就是代碼里可以操縱的,例如更改layer的屬性等等就在這一份魁索。
第二份融撞,動畫樹,這是一個中間層粗蔚,系統(tǒng)正在這一層上更改屬性尝偎,進(jìn)行各種渲染操作。
第三份鹏控,顯示樹致扯,這棵樹的內(nèi)容是當(dāng)前正被顯示在屏幕上的內(nèi)容。
這三棵樹的邏輯結(jié)構(gòu)都是一樣的当辐,區(qū)別只有各自的屬性抖僵。
*動畫的運作
UIView的主layer以外(我覺得是這樣),對它的subLayer缘揪,也就是子layer的屬性進(jìn)行更改耍群,系統(tǒng)將自動進(jìn)行動畫生成,動畫持續(xù)時間有 個缺省時間找筝,個人感覺大概是0.5秒蹈垢。在動畫時間里,系統(tǒng)自動判定哪些屬性更改了袖裕,自動對更改的屬性進(jìn)行動畫插值曹抬,生成中間幀然后連續(xù)顯示產(chǎn)生動畫效果。
*坐標(biāo)系系統(tǒng)(對position和anchorPoint的關(guān)系還是犯暈)
CALayer的坐標(biāo)系系統(tǒng)和UIView有點不一樣陆赋,它多了一個叫anchorPoint的屬性沐祷,它使用CGPoint結(jié)構(gòu),但是值域是0~1攒岛,也就是 按照比例來設(shè)置赖临。這個點是各種圖形變換的坐標(biāo)原點,同時會更改layer的position的位置灾锯,它的缺省值是{0.5, 0.5}兢榨,也就是在layer的中央。
某layer.anchorPoint = CGPointMake(0.f, 0.f);
如果這么設(shè)置顺饮,layer的左上角就會被挪到原來的中間的位置吵聪,
加上這樣一句就好了
某layer.position = CGPointMake(0.f, 0.f);
*layer可以設(shè)置圓角顯示,例如UIButton的效果兼雄,也可以設(shè)置陰影顯示吟逝,但是如果layer樹中的某個layer設(shè)置了圓角,樹中所有l(wèi)ayer 的陰影效果都將顯示不了了赦肋。如果既想有圓角又想要陰影块攒,好像只能做兩個重疊的UIView,一個的layer顯示圓角佃乘,一個的layer顯示陰 影.....
上面已經(jīng)說過了囱井,UIView之所以能夠顯示,完全是因為內(nèi)部的CALayer對象趣避。因此庞呕,通過操作這個CALayer對象,可以很方便地調(diào)整UIView的一些界面屬性程帕,比如:陰影住练、圓角大小、邊框?qū)挾群皖伾取?/p>
1.CALayer是被定義在QuartzCore框架中的澎羞,因此要想使用CALayer,先導(dǎo)入QuartzCore框架(如果已經(jīng)導(dǎo)入了UIKit框架,就無需導(dǎo)入QuartzCore框架,因為UIKit本身已經(jīng)引入QuartzCore框架?)
1> 點擊項目名稱敛苇,然后點擊右邊TARGETS下面的target
2> 點擊Build Pases后妆绞,展開Link Binary....,添加 + 號
3> 在搜索框中輸入"Quartz"枫攀,選中QuartzCore.framework括饶,最后add添加
4> 添加完畢后,這個框架就會出現(xiàn)在項目文件夾中
如果你覺得位置不好看来涨,還可以將它拖到Frameworks文件夾下图焰,跟其他框架放一起
2.在項目代碼中導(dǎo)入QuartzCore框架的主頭文件
3.通過CALayer修改UIImageView的界面屬性
你也可以使用UIButton或者UILabel,這里就以UIImageView為例子
1> 先創(chuàng)建一個UIImageView蹦掐,添加到控制器的view中
2> 設(shè)置陰影
* 第1行設(shè)置陰影的顏色為灰色技羔,注意僵闯,這里使用的是UIColor的CGColor屬性,是一種CGColorRef類型的數(shù)據(jù)
* 第2行設(shè)置陰影的偏移大小藤滥,可以看出陰影往原圖的右下角偏移
* 第3行設(shè)置陰影的不透明度為0.5鳖粟,表示半透明。如果為1拙绊,代表完全不透明向图。
3> 設(shè)置圓角大小
通過layer屬性可以訪問視圖內(nèi)部的CALayer對象
* 第1行設(shè)置圓角半徑為10
* 第2行的maskToBounds=YES:可以看做是強(qiáng)制內(nèi)部的所有子層支持圓角效果,少了這個設(shè)置标沪,UIImageView是不會有圓角效果的
* 注意榄攀,如果設(shè)置了maskToBounds=YES,那將不會有陰影效果
4> 設(shè)置邊框?qū)挾群皖伾?/p>
* 第1行設(shè)置邊框?qū)挾葹?
* 第2行設(shè)置邊框顏色為紅色
5> 設(shè)置旋轉(zhuǎn)
* 利用transform屬性可以設(shè)置旋轉(zhuǎn)金句、縮放等效果
* M_PI_4表示四分之π檩赢,順時針旋轉(zhuǎn)45°
* 后面的(0, 0, 1)表示Z軸這個向量,修改這個向量可以做一些三維旋轉(zhuǎn)效果趴梢,你可以隨便改個值試一下漠畜,比如(1, 1, 1)
* 總體的意思是layer會繞著Z軸順時針旋轉(zhuǎn)45°,也就是在x坞靶、y平面進(jìn)行旋轉(zhuǎn)
* 第1行創(chuàng)建了一個自動釋放的CALayer對象,你也可以使用經(jīng)典的alloc和init方法來創(chuàng)建
* 第12行將創(chuàng)建好的層添加到控制器的view的層中
* 在第7行設(shè)置需要顯示的圖片瘾敢,注意,這里用的是UIImage的CGImage屬性尿这,是一種CGImageRef類型的數(shù)據(jù)
三簇抵、為什么CALayer中使用CGColorRef和CGImageRef這2種數(shù)據(jù)類型,而不用UIColor和UIImage射众?
* 首先要知道:CALayer是定義在QuartzCore框架中的碟摆;CGImageRef、CGColorRef兩種數(shù)據(jù)類型是定義在CoreGraphics框架中的叨橱;UIColor典蜕、UIImage是定義在UIKit框架中的
* 其次,QuartzCore框架和CoreGraphics框架是可以跨平臺使用的罗洗,在iOS和Mac OS X上都能使用愉舔,但是UIKit只能在iOS中使用
* 因此,為了保證可移植性伙菜,QuartzCore不能使用UIImage轩缤、UIColor,只能使用CGImageRef、CGColorRef
* 不過很多情況下火的,可以通過UIKit對象的特定方法壶愤,得到CoreGraphics對象,比如UIImage的CGImage方法可以返回一個CGImageRef
細(xì)心的朋友不難發(fā)現(xiàn)公你,其實前面的2個效果不僅可以通過添加層來實現(xiàn),還可以通過添加UIView來實現(xiàn)假瞬。比如,第1個紅色的層可以用一個UIView來實現(xiàn)迂尝,第2個顯示圖片的層可以用一個UIImageView來實現(xiàn)脱茉。 既然CALayer和UIView都能實現(xiàn)相同的顯示效果,那究竟該選擇誰好呢垄开?
* 其實琴许,對比CALayer,UIView多了一個事件處理的功能溉躲。也就是說榜田,CALayer不能處理用戶的觸摸事件,而UIView可以
* 所以锻梳,如果顯示出來的東西需要跟用戶進(jìn)行交互的話箭券,用UIView;如果不需要跟用戶進(jìn)行交互疑枯,用UIView或者CALayer都可以
* 當(dāng)然辩块,CALayer的性能會高一些,因為它少了事件處理的功能荆永,更加輕量級
* UIView可以通過subviews屬性訪問所有的子視圖,類似地具钥,CALayer也可以通過sublayers屬性訪問所有的子層
* UIView可以通過superview屬性訪問父視圖豆村,類似地,CALayer也可以通過superlayer屬性訪問父層
* 下面再看一張UIView和CALayer的關(guān)系圖:
如果兩個UIView是父子關(guān)系骂删,那么它們內(nèi)部的CALayer也是父子關(guān)系掌动。
* 在前面幾講中已經(jīng)提到桃漾,每一個UIView內(nèi)部都默認(rèn)關(guān)聯(lián)著一個CALayer坏匪,我們可用稱這個Layer為Root Layer(根層)。所有的非Root Layer撬统,也就是手動創(chuàng)建的CALayer對象适滓,都存在著隱式動畫。
* 與UIView不同,CALayer實際上包含了一個表現(xiàn)層和一個模型層恋追。模型層是用來在內(nèi)存中存儲必要的圖層信息的凭迹。表現(xiàn)層則是用在將圖層顯示在屏幕上,并為此做了相應(yīng)的優(yōu)化罚屋。
如果說一個動畫是隱式動畫,那就意味著用做動畫的屬性是在模型層中被修改的然后在通過表現(xiàn)層傳遞出來,并最終顯示在屏幕上。 如果動畫是顯示動畫,進(jìn)行動畫的屬性就是只存在于表現(xiàn)層的,而原始的模型層(在進(jìn)行動畫之前的)會保持不變嗅绸。這就意味著除非做其他的動作,否則在一個顯示動畫結(jié)束之后,CALayer會回到動畫開始之前的狀態(tài),因為下面的模型層并沒有被修改.
* 當(dāng)對非Root Layer的部分屬性進(jìn)行相應(yīng)的修改時脾猛,默認(rèn)會自動產(chǎn)生一些動畫效果,這些屬性稱為Animatable Properties(可動畫屬性)鱼鸠。
* 列舉幾個常見的Animatable Properties:
bounds:用于設(shè)置CALayer的寬度和高度猛拴。修改這個屬性會產(chǎn)生縮放動畫
backgroundColor:用于設(shè)置CALayer的背景色。修改這個屬性會產(chǎn)生背景色的漸變動畫
position:用于設(shè)置CALayer的位置蚀狰。修改這個屬性會產(chǎn)生平移動畫
比如:假設(shè)一開始CALayer的position為(100, 100)愉昆,然后在某個時刻修改為(200, 200),那么整個CALayer就會在短時間內(nèi)從(100, 100)這個位置平移到(200, 200)
* 我們也可以從官方文檔中查詢所有的Animatable Properties
1.點擊Window -> Organizer
2.在搜索框輸入"animatable"即可
* position和anchorPoint屬性都是CGPoint類型的
* position可以用來設(shè)置CALayer在父層中的位置跛溉,它是以父層的左上角為坐標(biāo)原點(0, 0)
* anchorPoint稱為"定位點",它決定著CALayer身上的哪個點會在position屬性所指的位置扮授。它的x芳室、y取值范圍都是0~1,默認(rèn)值為(0.5, 0.5)
1.創(chuàng)建一個CALayer刹勃,添加到控制器的view的layer中
第5行設(shè)置了myLayer的position為(100, 100)堪侯,又因為anchorPoint默認(rèn)是(0.5, 0.5),所以最后的效果是:myLayer的中點會在父層的(100, 100)位置
注意荔仁,藍(lán)色線是我自己加上去的抖格,方便大家理解,并不是默認(rèn)的顯示效果咕晋。兩條藍(lán)色線的寬度均為100雹拄。
2.若將anchorPoint改為(0, 0),myLayer的左上角會在(100, 100)位置
3.若將anchorPoint改為(1, 1)掌呜,myLayer的右下角會在(100, 100)位置
4.將anchorPoint改為(0, 1)滓玖,myLayer的左下角會在(100, 100)位置
我想,你應(yīng)該已經(jīng)明白anchorPoint的用途了吧质蕉,它決定著CALayer身上的哪個點會在position所指定的位置上势篡。它的x、y取值范圍都是0~1模暗,默認(rèn)值為(0.5, 0.5)禁悠,因此,默認(rèn)情況下兑宇,CALayer的中點會在position所指定的位置上碍侦。當(dāng)anchorPoint為其他值時,以此類推。
方法描述:創(chuàng)建一個CALayer的子類瓷产,然后覆蓋drawInContext:方法站玄,使用Quartz2D API進(jìn)行繪圖
創(chuàng)建一個CALayer的子類
2.在.m文件中覆蓋drawInContext:方法,在里面繪圖
3.在控制器中添加圖層到屏幕上
注意第7行濒旦,需要調(diào)用setNeedsDisplay這個方法株旷,才會觸發(fā)drawInContext:方法的調(diào)用,然后進(jìn)行繪圖
方法描述:設(shè)置CALayer的delegate晾剖,然后讓delegate實現(xiàn)drawLayer:inContext:方法,當(dāng)CALayer需要繪圖時梯嗽,會調(diào)用delegate的drawLayer:inContext:方法進(jìn)行繪圖钞瀑。
* 這里要注意的是:不能再將某個UIView設(shè)置為CALayer的delegate,因為UIView對象已經(jīng)是它內(nèi)部根層的delegate慷荔,再次設(shè)置為其他層的delegate就會出問題。UIView和它內(nèi)部CALayer的默認(rèn)關(guān)系圖:
1.創(chuàng)建新的層缠俺,設(shè)置delegate显晶,然后添加到控制器的view的layer中
* 在第3行設(shè)置了CALayer的delegate,這里的self是指控制器
* 注意第9行壹士,需要調(diào)用setNeedsDisplay這個方法磷雇,才會通知delegate進(jìn)行繪圖
2.讓CALayer的delegate(前面設(shè)置的是控制器)實現(xiàn)drawLayer:inContext:方法
1.總結(jié)
無論采取哪種方法來自定義層躏救,都必須調(diào)用CALayer的setNeedsDisplay方法才能正常繪圖唯笙。
2.UIView的詳細(xì)顯示過程
* 當(dāng)UIView需要顯示時,它內(nèi)部的層會準(zhǔn)備好一個CGContextRef(圖形上下文)盒使,然后調(diào)用delegate(這里就是UIView)的drawLayer:inContext:方法崩掘,并且傳入已經(jīng)準(zhǔn)備好的CGContextRef對象。而UIView在drawLayer:inContext:方法中又會調(diào)用自己的drawRect:方法
* 平時在drawRect:中通過UIGraphicsGetCurrentContext()獲取的就是由層傳入的CGContextRef對象少办,在drawRect:中完成的所有繪圖都會填入層的CGContextRef中苞慢,然后被拷貝至屏幕
參考:
更多動畫知識: