前言html
當咱們使用核心動畫時,Layer對象是一切的核心。Layers 管理咱們APP的可視化content,Layer也提供了content樣式及content可視化的外觀的調整選項。儘管iOSAPP自動支持Layer,可是OS XAPP必須明確開啓Layer的使用才能利用這些相關的性能特色。一旦開啓Layer的使用,咱們須要去理解如何配置和操做Layers才能獲得想要的效果。後端
爲APP開啓核心動畫的支持緩存
在iOS APP裏,核心動畫是一直支持的而且每一個view 都關聯一個Layer(這種view被稱爲layer-backed view)。在OSX APP中,必須手動開啓核心動畫的支持,關聯QuartzCore framework。(iOS APP必須關聯這個framework僅當使用核心動畫接口時候)。app
開啓Layer的支持後,建立layer-backed view是其功能之一,這種View,系統將會負責爲其建立Layer對象並保持那個Layer的更新。框架
調整和View關聯的Layeriview
Layer-backed view 默認建立一個CALayer類的實例,大多數狀況下咱們不須要其餘樣式的Layer對象。然而,核心動畫提供了其餘的Layer類,各個Layer類都具備特殊的功能。選擇對應的Layer類有助於咱們提升性能,或者有助於以一種更簡單的方式顯示特殊樣式的content。例如CATiledLayer類爲高效的展現大圖而優化設計的。ide
改變Uiview的Layer類性能
咱們可以改變iOS View 的Layer類別經過重寫layerClass方式並一個不一樣的類。許多iOS Views建立一個CALayer對象並使用該對象做爲content的存儲。對於許多咱們本身的Views,默認的layer類別就是一個不錯的選擇。可是在某些特定的場景別的Layer類是更合適的。例如,咱們可能想要去改變Layer類在一下場景:優化
·咱們的View繪製content經過使用Metal 或者OpenGL ES,此時咱們須要使用CAMetalLayer 或者CAEAGLLayer 對象。動畫
·當有特定的Layer類提供更好的性能。
·咱們想要利用某些特定的核心動畫Layer類別。例如粒子發射或者粒子複製
改變View的Layer類的方式是很簡明的;就像2-1所示同樣,咱們須要作的只是重寫LayerClass方法而且返回咱們想要使用的類對象在展現以前View將會先調用layerClass方法,並使用它返回的類對象建立一個Layer對象,一旦建立後,該view的layer對象講不可更換。
+ (Class) layerClass { return [CAMetalLayer class]; }
相應的Layer類別的列表,和如何使用它們,參見 Different Layer Classes Provide Specialized Behaviors。
各類Layer類提供對應的特性
核心動畫定義了許多標準的Layer類,每個都具備特定的用途。CALayer是根類,它定義的特性其餘全部的Layer都必須支持,CALayer也是默認的類別。固然,咱們也可使用2-1表中的Layer類。
Class |
Usage |
---|---|
實現核心動畫中粒子發射系統,發射器Layer控制粒子的生成和他們的原點。 |
|
用來畫顏色的漸變,以便於用漸變的顏色填充layer上面的形狀(通常指繪製的圖形)。 |
|
用於設置和渲染可繪製的紋理(用於渲染layer 的content經過使用Metal) |
|
用於設置後端存儲和渲染layer 的content的上下文(OpenGL ES或者OpenGL ) |
|
當想要自動拷貝一個或多個子layer時,複製器建立副本,並使用咱們指定的屬性改變外觀或者副本的屬性。 |
|
用於管理一個由許多子layer複合而成的可滾動的區域。 |
|
用於畫一個三維或者二位的貝塞爾曲線。這種layer在繪製基於path形狀方面是有利的,由於他們總會建立出一個完善的path;而咱們將path繪製到某個layer的後臺存儲的,看起來將會有瑕疵當縮放的時候。然而,這個完善的性能牽涉到在主線程渲染該形狀並緩存這個結果。(我的以爲他應該是保存的矢量圖) |
|
用於渲染文本中普通的或者屬性字符串。 |
|
用於管理可以被分割成許多小塊並單獨渲染的大圖,並支持縮小和放大content. |
|
用於渲染真實的3D layer 圖層(而不是其餘layer類別所展示的平面圖層) |
|
用於渲染一個Quartz Composer 合成品 (OS X only)。 |
爲Layer提供contents
Layers是管理(由APP提供)content的數據對象。一個Layer的content由包含要被展示的可視化數據的bitmap所組成。咱們能夠經過如下三種方式提供bitmap:
·直接將一個image對象關聯到Layer對象的content屬性上。(對於不多或者不會改變的Layer content而言,這是最好的方式。)
·爲Layer關聯一個代理並讓這個代理繪製Layer的content。(對於可能按期或者偶爾改變的Layer content,或者要經過某個對象提供contnent,例如view,這是最好的方式。)
·定義一個Layer的子類而且重寫他的繪製方法以便提供Layer的contents。(若是咱們不得不建立一個Layer的子類,或者若是咱們想要去改變Layer基礎的繪製行爲,這將是最合適的方法。)
當咱們單首創建Layer對象時,也是惟一咱們須要考慮怎麼爲Layer提供content的時候,若是咱們的APP只包含layer-backed view,咱們沒必要考慮前面提到的如何提供Layer content的方法。Layer-backed views將會盡量的以最有效的方式爲他們關聯的Layer提供contents。
將一個image做爲Layer的content
由於一個Layer僅僅是一個管理bitmap image的容器,因此咱們直接爲Layer的contnent屬性關聯一個image。將一個圖片關聯到Layer是很便捷的,而且能讓咱們輕鬆的指定一個圖片顯示到屏幕上,Layer使用咱們直接提供大的Image,而且將不會建立那個圖片的副本。當咱們的APP在多個地方使用同一個圖片,這個方式能夠節約內存。
咱們關聯到Layer的圖片必須是一個CGImageRef類型。(在OSX v10.6和以後,咱們也能夠關聯NSImage對象。)當關聯某個圖片的時候,記住要提供和設備的分辨率相匹配的圖片。對於Retina屏幕的設備而言,咱們須要調整圖片的contentsScale屬性,更多關於爲Layer提供高分辨率contnet能夠參見 Working with High-Resolution Images。
經過代理爲Layer提供content
若是咱們的Layer須要動態改變,當須要改變的時候,咱們可使用代理對象來提供或者更新content,在顯示的時候,Layer經過調用代理的方法來提供所須要的content。
·若是代理實現了displayLayer:方法,那麼該方法的實現體須要爲建立一個bitmap並將該bitmap關聯到Layer的content屬性中。
·若是代理實現了 drawLayer:inContext:方法,核心動畫將會建立bitmap,以及建立用於繪製bitmap的圖形context,並調用這個代理方法填充bitmap,此代理方法須要作的就是將所需內容繪製到圖形context中。
代理對象必須實現displayLayer: 或者drawLayer:incontext:方法,若是代理同時實現了這兩個方法,那麼代理僅僅只會執行displayLayer:方法。
當咱們的APP須要加載或者建立要顯示的bitmap的時候,重寫displayLayer:方法是最合適的方法。代碼清單2-3展現了displayLayer:代理方法的簡單實現過程,在這個例子中,代理對象使用一個協助對象來加載所須要的image。代理方法選擇顯示哪一個image取決於它自身的狀態變量,這個變量在例子中就是這得自定義的屬性displayYesImage。
- (void)displayLayer:(CALayer *)theLayer {
// Check the value of some state property
if (self.displayYesImage) {
// Display the Yes image
theLayer.contents = [someHelperObject loadStateYesImage];
}
else {
// Display the No image
theLayer.contents = [someHelperObject loadStateNoImage];
}
}
若是咱們沒有預先渲染bitmap,或者也沒有供建立bitmaps的協助對象,那咱們須要經過drawLayer:incontext:方法實現動態繪製content。代碼清單2-4展現了drawLayer:incontext:方法的實現。在這個例子中,代理使用設定的寬度和渲染顏色繪製了一個簡單的曲線path。
- (void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext { CGMutablePathRef thePath = CGPathCreateMutable(); CGPathMoveToPoint(thePath,NULL,15.0f,15.f); CGPathAddCurveToPoint(thePath, NULL, 15.f,250.0f, 295.0f,250.0f, 295.0f,15.0f); CGContextBeginPath(theContext); CGContextAddPath(theContext, thePath); CGContextSetLineWidth(theContext, 5); CGContextStrokePath(theContext); // Release the path CFRelease(thePath); }
對於須要自定義content的layer-backed views而言,咱們仍然須要充寫view的方法來實現繪製。一個Layer-backed view將他的Layer的代理關聯給他自己,並實現了所需的代理方法,並且咱們也不須要手動改變這個配置,不過,咱們須要實現這個layer-backed view的drawRect:方法來繪製咱們的content。
經過子類提供Layer 的content
若是咱們須要實現一個自定義的Layer,咱們能夠重寫Layer的繪製方法作各類各樣的繪製。不一樣於Layer對象自身生成自定義的content,layer須要管理這個用於顯示的content(這句話應該是指的是相對於指定image提供content的方式)。例如,CATiledLayer類管理一個大的image的原理是:經過講image分隔成許多小的能夠單獨管理和繪製的片斷,因爲這個Layer有關於在什麼時候須要渲染那一個片斷的信息,他直接管理了整個繪製行爲。
當自定義子類的時候,咱們須要實現下面兩種繪製content的方式之一:
·重寫子類的display方法,並直接在該方法中設置contents屬性。
·重寫子類的drawInContext:方法而且使用他在該圖形context中進行繪製。
選擇重寫那種方法取決於咱們須要控制繪製過程的程度。display方法是對於更新Layer的content的徹底控制,所以重寫該方法將使咱們徹底控制繪製的過程。重寫display方法也意味着咱們須要建立CGImageRef並將其關聯到content屬性。若是咱們僅僅想要繪製content(或者讓咱們的layer管理繪製操做),咱們能夠重寫drawInContext:方法,並讓Layer建立後備存儲。
對咱們提供的content進行調整
當將一個image關聯到Layer的content屬性時候,Layer的contentGravity屬性決定了那個圖片將被如何調整爲了適應當前的bounds。默認的若是一個圖片是比當前的bounds更大或者更小的,Layer對象就會縮放這個圖片以便適應可用的空間,若是Layerbounds的寬高比是不一樣於image的寬高比,這將會引發圖片顯示效果不徹底,咱們可使用contentsGravity屬性來確保content以最好的方式展示。
可供使用的contentsGravity類型被分爲兩大種:
·基於position的重力常量,使咱們能夠在無縮放的狀況下,從bounds的某個邊或者角鋪展image。
·基於縮放的重力常量,使咱們能夠以某幾種方式之一伸縮image,有些選項能夠維持寬高比,有些將不維持image的原有寬高比。
圖2-1展現了基於點的重力設置對image的影響。除了kCAGravityCenter常量的使用,其餘的常量將會以image bounds的某個邊或者角來鋪展image。kCAGravityCenter常量將image從中心開始鋪展。這類常量不會引發圖片的伸縮,所以image會按照原來的尺寸進行渲染。若是image是大於Layer的bounds,這將會致使image一部分被裁減掉,若是image的尺寸是小於Layer的bounds,若是設置了layer的背景色,空缺的部分將會顯示Layer的背景色。
圖2-2展現了基於縮放的重力常量如何影響image顯示的。若是image的尺寸和Layer的bounds不一致,全部的這些常量將會縮放image。這些常量的不一樣之處在於如何調整這些圖片原來的寬高比,其中有些模式是保持原來的寬高比,還有一些將會改變原來的寬高比。默認狀況下Layer的contentsGravity屬性是被設置爲kCAGravityResize 常量,這是該類型中惟一不保持圖片原有寬高比的圖片。
如何使用高分辨率的Images
Layers自己並不知道所在設備屏幕分辨率。Layer僅僅存儲指向bitmap的指針並使用所給定的可利用的像素按照儘量好的方式展示。若是咱們將一個Image關聯到一個Layer的content屬性,咱們必須經過設置Layer的contentScale屬性告訴核心動畫Image的分辨率。ContentScale屬性的默認值是1.0,這隻適用於那些將要顯示到標準分辨率屏幕的Image,若是咱們的Image想要顯示到Retina屏幕上,那麼咱們就必須設置這個屬性爲2.0。
若是咱們直接關聯一個bitmap到Layer,那麼咱們就須要設置contentScale的屬性。爲了適配屏幕的分辨率和被View管理的content,UIKit 和AppKit框架中 layer-backed view會自動設置Layer的縮放因子。例如,在OSX中,若是咱們關聯一個NSImage對象到Layer的contnet屬性,AppKit將會查找標準和更高分辨率的Image,若是都有,那麼AppKit將會使用正確的分辨率的image來設置contentScal的屬性。
調整Layer的可視化風格和展現樣式
Layer對象能夠建立可視化的配件來增添Layer的主要contents,例如邊框和背景色。這些可視化的配件不須要咱們本身作任何的渲染工做,所以在某些狀況下就可使用Layer做爲單獨的總體。全部須要咱們作的僅僅是設置Layer的屬性,Layer將會處理所需的繪製工做,固然也包含任何動畫。對於可視化配件是如何影響Layer顯示的說明能夠參見 Layer Style Property Animations。
Layer有他們本身的背景和邊框
除了他的基於image的content以外,一個Layer能夠顯示填充的背景和填充的邊框。背景色是在Layer的content後面渲染的,邊框是在image的content的上面渲染的,就像圖2-3所示。若是Layer包含子Layer,他們也出如今邊框的下面。因爲背景色是放置在image的後面的,image的透明的部分將會顯示背景色。
代碼清單2-5顯示設置Layer的背景色和邊框所須要的代碼,全部這些屬性都是可動畫的。
myLayer.backgroundColor = [NSColor greenColor].CGColor; myLayer.borderColor = [NSColor blackColor].CGColor; myLayer.borderWidth = 3.0;
注意:咱們可使用任何顏色做爲Layer的背景色,包括帶有透明份量的顏色或者使用樣品image。當使用樣品image的時候,要注意Core Graphics 處理圖片的渲染,以及在此處理圖片渲染的過程當中將使用標準座標系統,標準座標系統和iOS中默認的座標系統是不一樣的。在iOS上默認狀況下,圖片渲染是上下顛倒的,除非咱們調整座標系統。
若是咱們設置Layer的背景色爲不透明的顏色,那麼咱們應該講Layer的opaque屬性設置爲YES,這麼作將會提高性能當合成該Layer到屏幕上顯示時候,並消除了layer 做爲輔助存儲時候管理的透明通道。若是Layer有非0的圓角,咱們就沒必要將該Layer標記爲不透明。
Layer支持圓角
經過添加corner radius,咱們能夠爲Layer建立圓角效應。corner radius是一個可視化配件,它能夠遮蓋Layer的四角讓下面的content顯示出來。就像圖2-4顯示的。它涉及到透明遮罩的應用,corner radius不影響image的Layer中的content屬性,除非設置masksToBounds屬性爲YES。然而,corner radiu 一直影響Layer的背景色和邊框的繪製。
爲了將corner radius應用到Layer,咱們須要爲Layer的cornerRadius屬性指定一個值。圓角的指定單位是points,而且顯示的時候,它將會預先應用到Layer的四個角。
Layer支持內建陰影
CALayer 類包含幾個爲配置陰影效應的屬性。陰影會經過讓它看起來像是漂浮在content下面的方式來爲Layer添加深度。這是另一種可視化的配件,當APP須要的時候可使用。對於Layer而言咱們能夠控制陰影的顏色,相對於content的位置,不透明度,和形狀。
Layer陰影的不透明度默認被設置爲0,這將會有效的隱藏陰影。改變一個透明度爲非零的值,將會引發核心動畫繪製陰影。陰影默認是被直接放置在Layer的下面的,爲了可以看到陰影,咱們也須要調整陰影的偏移量。有個很重要的事情須要記住,咱們爲陰影指定的偏移量是基於Layer的本地座標系統的,也就是說在iOS和OSX 上面是不一樣的。如圖2-5展現了一個偏向右下的陰影,在iOS中,須要指定一個正數在Y軸份量上,可是在OSX 上就須要指定爲負值。
當爲Layer添加陰影的時候,陰影就是Layer的content的一部分,可是實際上陰影有可能超出Layer的邊界,若是開啓Layer的maskToBounds屬性那麼陰影的效應將會在Layer的邊界處被裁剪,這將會產生一個奇怪的現象,那就是在Layer的內部的陰影是顯示的,可是在Layer的邊界外部的陰影缺看不到。若是咱們想要顯示完整的陰影而且還想使用bounds masking,咱們就應該使用兩個Layer而不是一個,將mask應用到包含content的Layer上,而後將這個Layer嵌入到第二個Layer上,第二個Layer的尺寸和第一個的尺寸同樣,第二個Layer上設定陰影效果。
更多關於如何Layer上如使用陰影可參見Shadow Properties。
爲Layer添加自定義屬性
CAAnimation 和CALayer 類延伸了KVC以便支持自定義屬性。咱們能夠其使用這個特性爲Layer添加數據,而後經過自定義的屬性獲取該數據。咱們甚至能夠關聯actions到自定義的屬性中,進而達到當修改這個屬性的時候,對應的動畫將會被執行。
爲更多的信息關於如何使用和設置自定義屬性,能夠參見 Key-Value Coding Compliant Container Classes。爲更多的信息關於添加actions到Layer對象,能夠參見 Changing a Layer’s Default Behavior。
打印Layer-backed View的內容
在打印期間,Layer 會重繪他們的contents爲了適配打印環境。核心動畫正常狀況下依靠緩存的bitmap當渲染到屏幕上的時候,然而他將會重繪製content當打印的時候。尤爲,layer-backed view使用drawRect:方法提供Layer 的content,當打印的時候,核心動畫將再次調用drawRect:來生成須要打印的Layer的content。