本文首發地址html
圖層對象是你用 Core Animation 所作的一切事情的核心。圖層管理你 app 的視覺內容,提供視覺內容樣式和視覺外觀的修改選項。雖然 iOS app 內嵌自動支持圖層,可是 OS X 上面的 app 開發者必須在利用圖層的性能優點前手動讓圖層生效。一旦生效,你須要理解如何配置和操做你 app 的圖層,來獲取你想要的效果。git
在 iOS 的 app 中, Core Animation 老是自動生效的,而且每一個視圖是由圖層支持的。在 OS X 中,app 必須經過下面的操做來使 Core Animation 生效:github
setWantsLayer:
方法傳遞 YES 來講明該視圖應該使用圖層。圖層支持的視圖默認會建立 CALayer 的實例,多數狀況下你不須要去改變圖層對象的類型。但,Core Animation 提供了一些不一樣的圖層類,每個提供的圖層類都是頗有用處的。選擇不一樣的圖層類可能會提升你的代碼性能,或者經過簡單的方式支持一種指定的內容類型。例如, CATiledLayer 類經過高效的方式用來優化大型圖片的顯示。數組
你能夠經過重寫視圖的 layerClass
方法返回一個不一樣的類對象,來改變 iOS 上視圖使用的圖層類。大多數的 iOS 視圖建立一個 CALayer 對象,並使用該對象支持存儲它的內容。對於你的大多數視圖,默認的選擇是不錯的,你不須要去改變它。可是在某些狀況下,你會發現一個不一樣的圖層類是更加合適的。例如,如下幾種狀況你可能想改變圖層類:緩存
改變視圖的圖層類是很是簡單的;能夠參見 例2-1 的例子。你須要作的就是重寫 layerClass
方法,返回一個你想使用的類對象。顯示以前,視圖會調用 layerClass
方法,使用返回的類建立一個新的圖層對象。視圖的圖層對象一旦被建立,就不可修改。bash
例 2-1 改變視圖的圖層類app
// OC
+ (Class) layerClass {
return [CAMetalLayer class];
}
// Swift
override class var layerClass: AnyClass {
return CAMetalLayer.self
}
複製代碼
關於圖層類的列表和你如何使用它們,可參見不一樣的圖層類提供專門的行爲。框架
你能夠經過重寫 NSView 對象的 makeBackingLayer
方法來改變它的默認圖層類。在該方法裏,建立、返回一個你想 AppKit 去用來支持你自定義視圖的圖層對象。你可能如下狀況須要重寫該方法:使用相似滾動圖層(scrolling)或者平鋪圖層(tiled layer)的自定義圖層。關於圖層類的列表和你如何使用它們,可參見不一樣的層類提供專門的行爲。ide
當你的視圖調用 setLayer:
方法而且提供了一個圖層對象時,AppKit 將不會管理該圖層對象。一般,AppKit 會更新視圖的圖層對象,但對於圖層託管的視圖,大多數屬性不會更新。post
爲了建立一個圖層託管視圖,你須要建立本身的圖層對象,並在視圖在屏幕顯示以前將圖層和視圖關聯起來,如例 2-2 中代碼所示。爲了設置圖層對象,你必須調用 setWantsLayer:
方法讓視圖知道它應該使用圖層。
例 2-2 建立一個圖層託管視圖
// Create myView...
[myView setWantsLayer:YES];
CATiledLayer *hostedLayer = [CATiledLayer layer];
[myView setLayer:hostedLayer];
// Add myView to a view hierarchy.
複製代碼
若是你選擇使用圖層託管視圖,你必須本身設置 contentsScale
屬性,而且在合適的時機提供高分辨率的內容。關於高分辨率內容和倍率的更多內容,請參見使用高分辨率圖像。
表 2-1 CALayer 的子類和它們的使用
類名 | 具體用途 |
---|---|
CAEmitterLayer | 用於實現基於核心動畫的粒子發射器系統。發射器層對象控制粒子的生成及其來源 |
CAGradientLayer | 用於繪製填充圖層形狀的顏色漸變(在任何圓角的邊界內) |
CAMetalLayer | 用於設置和提供可繪製紋理,以使用 Metal 渲染圖層內容 |
CAEAGLLayer/CAOpenGLLayer | 用於使用OpenGL ES (iOS) 或 OpenGL (OS X)設置渲染層內容的後備存儲和上下文 |
CAReplicatorLayer | 用於當你想自動拷貝一個或者多個子圖層時。拷貝器會爲你製做副本,而且使用你指定的屬性值去修改副本的外觀或屬性 |
CAScrollLayer | 用於管理一個由多個子圖層組成的大的、可滑動的區域 |
CAShapeLayer | 用於繪製立方形的貝斯曲線。 Shape layers 有利於繪製基於路徑的形狀,由於它們老是產生清晰的路徑,而不是繪製到層的後備存儲中的路徑,後者在縮放時看起來不太好。然而,清晰的結果確實包括在主線程上渲染形狀和緩存結果 |
CATextLayer | 用於繪製文本或者富文本 |
CATiledLayer | 用於管理大圖像,該圖像可分爲較小的圖塊,並經過支持放大和縮小內容來單獨呈現 |
CATransformLayer | 用於渲染真正的3D圖層層次,而不是由其餘圖層類實現的2D圖層層次 |
QCCompositionLayer | 用於渲染 Quartz Composer 結構(僅限 OS X) |
圖層是管理由你 app 提供的內容的數據對象。圖層的內容由包含要顯示的視覺數據的位圖組成。你能夠經過下面三種的任意一種方式來提供包含內容的位圖:
contents
屬性賦值一個 image 對象。(該方法適用於圖層內容不改變或者不多改變的狀況)視圖爲圖層的 delegate
)你僅須要本身建立圖層對象的時候去考慮爲圖層提供內容。若是你的 app 中都是圖層支持的視圖,你不須要擔憂使用上面提到的方法。圖層支持視圖會用高效的方法自動提供關聯圖層的內容。
由於圖層只是管理位圖圖像(bitmap image)的一個容器,你能夠直接給圖層的 contents
屬性賦值 image 對象。給圖層賦值一個 image 是很是容易的,而且容許你指定想顯示在屏幕上確切的 image 。圖層使用你直接提供的 image 對象,不會試圖建立 image 的副本。當你在多個地方使用同一個 image 時,這種行爲會節省你的內存。
你賦值給圖層的 image 必須是 CGImageRef
類型(在 OS X v10.6 及之後,你也能夠賦值 NSImage
對象)。當賦值 image 時,記住需提供與本機分辨率匹配的圖像。對於 Retina 顯示屏,可能須要你去調整 image 對象 contentsScale
屬性的值。關於對圖層使用高分辨率內容的更多信息,請參見使用高分辨率圖像。
若是圖層內容動態改變,你能夠在須要的時候使用 delegate 對象來提供、更新圖層內容。在顯示時間,圖層會調用 delegate 的方法去提供的須要的內容:
displayLayer:
方法,該方法會建立位圖並將它賦值給圖層的 contents
屬性。drawLayer:inContext:
方法,Core Animation 會建立位圖,而後建立一個圖形上下文(graphics context)來繪製該位圖,最後調用 delegate 方法填充位圖。delegate 方法必須在繪製到提供的圖形上下文中。delegate 對象必須實現 displayLayer:
或 drawLayer:inContext:
方法。若是 delegate 實現了這兩個方法,圖層只會調用 displayLayer:
方法。
當 app 去加載或者建立想顯示的位圖時,重寫 displayLayer:
方法是很是適合的。例 2-3 簡單展現瞭如何實現 displayLayer:
方法。在該例中,delegate 使用了 helper 對象去加載顯示它須要的 image。delegate 基於它內部狀態來選擇顯示哪一個 image,在該例中,內部狀態是叫作 displayYesImage
的自定義屬性。
例 2-3 直接設置圖層內容
- (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];
}
}
複製代碼
若是你沒有預渲染的 image 或者 helper 對象去建立位圖,delegate 可使用 drawLayer:inContext:
方法動態繪製圖層內容。例 2-4 展現了關於 drawLayer:inContext:
方法實現的簡單例子。在該例中,delegate 使用固寬和當前渲染顏色繪製了一個簡單的曲線路徑。
例 2-4 繪製圖層內容
- (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);
}
複製代碼
對有自定義內容的圖層支持視圖,應該繼續重寫視圖的方法來作繪製。圖層支持視圖自動將本身設置爲關聯圖層的 delegate,而且實現了須要實現的方法,你不該改變配置。你應該實現 drawRect:
方法來繪製你的內容。
在 OS X v10.8及之後的版本,繪圖的另外一個選擇是:經過重寫視圖的 wantsUpdateLayer
和 updateLayer
方法提供位圖。重寫 wantsUpdateLayer
並返回 YES ,由於 NSView 類遵循備用渲染路徑。代替調用 drawRect:
,視圖調用 updateLayer
方法,該方法必須直接給圖層 contents
屬性賦值位圖。在這種狀況下,AppKit 但願直接設置視圖圖層對象的內容。
若是你實現了自定義圖層類,你能夠重寫你圖層類的繪製方法來作任何繪製。對圖層對象來講,生成自己自定義內容並不常見,但圖層固然能夠管理顯示的內容。例如,CATiledLayer 類經過將大圖像分割成若干小圖像(所以能夠獨立管理和渲染它們)的方法來管理大圖像。由於只有圖層知道哪個小圖像在給定的時間上被渲染,因此圖層直接管理繪製行爲。
當建立自定義子類時,能夠經過下面的兩種方法來繪製圖層內容:
display
方法,使用該方法直接給圖層的 contents 屬性賦值。drawInContext:
方法,使用該方法繪製到提供的圖形上線文中。重寫哪個方法取決於你須要控制多少繪製過程。 display
方法是更新圖層內容的主入口點,因此重寫該方法能夠完整的控制繪製過程。重寫 display
方法也意味着你須要建立賦值給 contents
屬性的 CGImageRef
類型的對象。若是你只是想繪製內容(或者讓圖層管理繪製操做)你能夠重寫 drawInContext:
方法,讓圖層建立備用存儲(backing store)給你。
當你給圖層的 contents
屬性賦值圖像時,圖層的 contentsGravity
屬性決定如何操做圖像以配當前邊界的。默認狀況下,若是圖像比當前邊界更大或者更小,圖層對象會將圖像調整到有效空間內。若是圖層邊界的寬高比(aspect ratio)與圖像的寬高比不一樣,會形成圖像的變形。你可使用 contentsGravity
屬性確保內容獲得了最好的展現。
能夠賦給 contentsGravity
屬性的值被分爲兩個類:
圖 2-1 展現了基於位置的重力設置如何影響圖像。除了 kCAGravityCenter 常量以外,每一個常量都將圖像固定在圖層邊界矩形的特定邊緣或角上。kCAGravityCenter 常量將圖像置於圖層中心。這些常數都不會使圖像縮放,因此圖像老是以原尺寸進行渲染。若是圖像比圖層的邊界大,可能會形成圖像的部份內容被裁減;若是圖像比圖層的邊界小,圖層未被圖像覆蓋的部分會填充圖層的背景色(若是設置的話)。
圖 2-2 顯示了基於縮放的重力常量如何影響你的圖像。若是圖像尺寸沒有徹底和圖層的尺寸一致的話,全部的常量都會縮放圖像。它們直接的不一樣就是如何影響圖像的原始寬高比。一些會保持寬高比,一些則不會。默認狀況下,圖層的 contentsGravity
屬性設置爲 kCAGravityResize
常量,它是惟一一個不保持圖片寬高比的常量。
圖層對於設備屏幕的分辨率一無所知。圖層只存儲了位圖的指針,用給定的可用像素儘量最好的方式顯示它。若是你給圖層的 contents
屬性賦值了圖像,你必須告訴 Core Animation 由圖層 contentsScale
屬性設定的合適的值來決定的圖像分辨率。該屬性默認值爲 1.0 ,對於顯示在標準分辨率屏幕上的圖像來講,它是一個合適的值。若是你的圖像放在 Retina 屏上,應該把值設置爲 2.0。
僅在你直接給圖層賦值位圖的時候修改 contentsScale
屬性的值。UIKit 和 AppKit 裏圖層支持的視圖會基於屏幕分辨率和視圖管理的內容自動給圖層的設置一個合適的比例值。例如,若是你給 OS X 的圖層 contents 屬性值賦值 NSImage 對象,APPKit 會觀察圖像是否包含標準和高分辨兩個類型的圖像。若是有,APPKit 會使用正確的圖像以適配當前屏幕,也會給 contentsScale
屬性設置適配當前屏幕的值。
在 OS X 裏,基於位置的重力常數會影響從指定給圖層的圖像對象中選擇圖像表示的方式。由於這些常數不會致使圖像縮放,因此 Core Animation 依賴於 contentsScale
屬性來選擇具備最合適像素密度的圖像表示。
在 OS X 裏,圖層的 delegate 能夠實現 layer:shouldInheritContentsScale:fromWindow:
方法,並使用該方法去改變比例值。不管什麼時候給定 window 的分辨率改變,AppKit 都會自動調用該方法, 這是可能的,由於圖像會在標準分辨率和高分辨率的屏幕上移動。若是 delegate 支持改變圖層圖像的分辨率,那實現方法應該返回 YES 。當須要去反應新的分辨率時,該方法應該及時更新圖層的內容。
圖層對象有內嵌的視覺裝飾,例如你能夠在圖層的主內容上添加邊界線和背景色。由於這些視覺裝飾不要求你作任何的渲染,因此它們使得在某些狀況下能夠將圖層用做獨立實體。你所須要作的就是設置圖層的屬性值,塗岑會處理必要的繪製,包括任何動畫。有關這些視覺裝飾如何影響圖層外觀的其餘說明,請參見圖層樣式屬性動畫。
圖層能夠在基於圖像的內容上顯示填充滿的背景色和描繪邊框。背景色在圖像內容後面繪製,邊界線會在圖像的上方繪製,如圖 2-3 所示。若是圖層包含子圖層,它們也會出如今邊界線的下方。由於背景色位於圖像的後面,因此它會透過任何圖像透明的部分。
例 2-5 展現了設置圖層背景色和邊界線所需的代碼。這些都是能夠進行動畫的。
例 2-5 設置圖層的背景色和邊界線
// OC
myLayer.backgroundColor = [NSColor greenColor].CGColor;
myLayer.borderColor = [NSColor blackColor].CGColor;
myLayer.borderWidth = 3.0;
//Swift
myLayer.backgroundColor = NSColor.green.cgColor
myLayer.borderColor = NSColor.black.cgColorlor
myLayer.borderWidth = 3.0
複製代碼
注意:對圖層的背景色你可使用任意類型的顏色,包括有透明度的顏色或者使用圖案圖像。當使用圖案圖像(pattern image)時, 要注意到Core Graphics 會使用標準座標系處理圖案圖像的渲染,這和 iOS 上默認的座標系是不一樣的。所以,除非你反轉座標,不然在 iOS 上渲染的圖像默認會顛倒顯示。
若是你給圖層背景色設置了不透明顏色,考慮將圖層的 opaque 屬性設置爲 YES 。這樣作能夠提升屏幕合成圖層時的性能,並消除圖層後臺存儲管理 alpha 通道的須要。可是,若是圖層的角半徑也非零,則不能將其標記爲不透明。
你能夠經過給圖層添加一個角半徑來實現矩形圓角的效果。角半徑是視覺修飾,經過遮罩圖層邊界角的一部分來容許底層內容的顯示,如圖 2-4 顯示。由於它涉及透明遮罩,因此角半徑不會影響圖層 contents
屬性的 image,除非 masksToBounds 屬性值爲 YES 。但,角半徑老是會影響圖層的背景色和邊線的繪製。
cornerRadius
屬性的值能夠爲圖層設置角半徑。你指定的半徑值使用點爲單位,而且在圖層顯示前會應用到圖層的四個角。
CALayer 包含幾個屬性能夠用來配置陰影效果。陰影經過使圖層看起來像漂浮在底層內容之上來增長圖層的深度。這是另外一個視覺裝飾,在某些狀況下這是很是有用的。經過圖層,你能夠控制陰影的顏色、相對於圖層內容的位置、透明度和形狀。
圖層陰影的透明度的值默認爲 0,這會有效的隱藏陰影。將透明度改爲一個非零值,會使 Core Animation 繪製陰影。由於陰影默認的位置是在圖層下面的,在你看到它以前,須要你改變陰影的偏移量。記住下面這一點是很重要的,你指定陰影的偏移量是使用圖層原始座標系來應用的,而 iOS 和 OS X 是不一樣的。圖 2-5 顯示了在右下方有陰影的圖層。在 iOS 上,y 軸的值應該是正數,而在 OS X 上面 y 軸的值應該爲負數。
當在圖層上添加陰影時,陰影是圖層內容的一部分,但實際上它會延展到圖層邊界以外。所以,若是你將圖層的 masksToBounds
屬性設置爲 YES ,邊界以外陰影的效果也會被剪切掉。若是你的圖層包含任何透明內容,會形成一個奇怪的效果:在圖層下面陰影的部分仍然可見,可是在圖層邊界以外的是不可見的。若是你想同時使用陰影和邊界蒙版,你應該用兩個圖層來代替。對包含你內容的圖層應用蒙版,再將該圖層嵌入到有陰影效果的徹底相同尺寸的圖層中。
關於如何對圖層應用陰影的例子,請參見陰影屬性。
在 OS X app 裏,你能夠直接對圖層內容應用 Core Image 過濾器。你能夠經過過濾器來使圖層內容模糊或者銳化,去改變顏色、扭曲內容、執行不少其餘類型的操做。例如,圖像處理程序可能會使用過濾器無損的修改圖像,視頻編輯程序可能使用它們實現不一樣類型的視頻過渡效果。而且由於過濾器是用硬件應用在圖層內容上的,因此渲染是很快且平滑的。
注意:你不能給 iOS 上的圖層添加過濾器。
對於給定的圖層,你能夠對圖層內容的前景和背景應用過濾器。前景內容由圖層本身包含的全部內容組成,包含 contents 屬性的圖像、背景色、邊界線和子圖層的內容。背景內容是直接位於圖層下方但實際上不是圖層自己的一部分的內容。大多數圖層的背景內容是它的直接父圖層,其父圖層可能整個或者部分被其遮罩。例如,當你想用戶專一於圖層前景內容時,會對圖層背景應用模糊過濾器。
你能夠經過下面的圖層屬性來添加 CIFilter 類型的指定過濾器:
filters
屬性包含一個數組,數組元素爲僅能影響圖層前景的過濾器。backgroundFilters
屬性包含一個數組,數組元素爲僅能影響圖層背景的過濾器。compositingFilter
屬性定義了圖層的前景和背景內容如何被組合在一塊兒。爲了給圖層添加過濾器,你必須定位、建立 CIFilter 對象,並在對象添加到圖層以前將它配置好。 CIFilter 類包含幾個用來定位可用 Core Image 過濾器的類方法,如 filterWithName:
。建立過濾器只是第一步,不少過濾器有定義過濾器如何修改圖像的參數。例如,盒子模糊過濾器有一個輸入半徑的參數,該參數用於應用的過濾器的數量。做爲過濾器配置過程的一部分,你應該始終提供這些參數的值。可是,一個不須要指定的常見參數是輸入圖像,它由圖層自己提供。
當向圖層添加過濾器時,最好在過濾器添加到圖層以前配置過濾器的參數。主要緣由是:一旦將過濾器添加到圖層上,你就不能 CIFilter 對象自己來修改它了。可是,在添加到圖層以後,你可使用圖層的 setValue:forKeyPath:
方法來修改。
例 2-6 展現瞭如何建立收縮失真濾波器並將其應用到圖層上。此濾鏡向內收縮圖層的源像素,最大程度地扭曲最靠近指定中心點的像素。注意在該例中你不須要指定過濾器的輸入圖像,由於圖層的圖像會被自動使用。
例 2-6 對圖層應用過濾器
CIFilter* aFilter = [CIFilter filterWithName:@"CIPinchDistortion"];
[aFilter setValue:[NSNumber numberWithFloat:500.0] forKey:@"inputRadius"];
[aFilter setValue:[NSNumber numberWithFloat:1.25] forKey:@"inputScale"];
[aFilter setValue:[CIVector vectorWithX:250.0 Y:150.0] forKey:@"inputCenter"];
myLayer.filters = [NSArray arrayWithObject:aFilter];
複製代碼
有關可用 Core Image 過濾器的信息,請參見Core Image Filter 參考。
在 OS X 裏,圖層支持視圖支持幾個不一樣的策略,用來決定什麼時候更新底部圖層的內容。由於原生 AppKit 繪製模型和由 Core Animation 介紹的方法是不一樣的,這些策略使你將老代碼遷移到 Core Animation 技術上變得更加簡單。你能夠逐個視圖地配置這些策略,以確保每一個視圖的最佳性能。
每一個視圖都定義了 layerContentsRedrawPolicy
方法,用來返回是圖圖層的重繪策略。你可使用 setLayerContentsRedrawPolicy
方法來設置策略。爲了保持與傳統繪圖模型的兼容性,AppKit 默認狀況下會將重繪策略設置爲 NSViewLayerContentsRedrawDuringViewResize
。可是,你能夠將策略改成表 2-2 的任意一個值。注意推薦的重繪策略並非默認的那個。
表 2-2 OS X 視圖的圖層重繪策略
策略 | 用法 |
---|---|
NSViewLayerContentsRedrawOnSetNeedsDisplay | 這是推薦的策略。用該策略,視圖幾何圖形的改變不會自動形成視圖去更新圖層的內容。取而代之的是,拉伸和操縱圖層的現有內容,以方便幾何圖形的更改。想強制視圖重繪它自身,並更新圖層的內容,你必須顯示的調用圖層的 setNeedsDisplay 方法。 該策略最接近於 Core Animation 圖層的標準行爲。可是,它不是默認策略,必須顯式設置。 |
NSViewLayerContentsRedrawDuringViewResize | 這是默認的重繪策略。該策略經過不管什麼時候視圖幾何圖形改變,都會從新緩存圖層的內容的方法,與傳統 AppKit 繪製保持了最大的兼容性。該行爲會形成視圖的 drawRect: 方法在從新設置尺寸期間在 app 主線程屢次調用。 |
NSViewLayerContentsRedrawBeforeViewResize | 使用該策略,在任何調整尺寸的操做以前,AppKit 以最終尺寸繪製圖層,並緩存該位圖。調整尺寸操做使用緩存位圖做爲起始圖像,縮放它以適應舊的邊界矩形。而後,它將位圖動畫化爲最終尺寸。這種行爲會致使視圖內容在動畫開始時出現拉伸或扭曲,在初始外觀不重要或不明顯的狀況下效果會更好。 |
NSViewLayerContentsRedrawNever | 使用策略,即便調用 setNeedsDisplay: 方法,AppKit 也不會更新圖層。此策略最適合內容從不改變的視圖,以及視圖大小不多改變(若是有的話)的視圖。例如,你能夠將它用於顯示固定大小內容或背景元素的視圖。 |
視圖重繪策略減輕了使用獨立子圖層來提升繪圖性能的須要。在引入視圖重繪策略以前,有些圖層支持視圖比須要的繪製頻率更高,從而致使性能問題。解決這些性能問題的方法是使用子圖層來呈現視圖內容中不須要按期重繪的部分。隨着重繪策略在 OS X v10.6 中的引入,如今建議將圖層層支持視圖的重繪策略設置爲適當的值,而不是建立顯式子圖層結構。
CAAnimation 和 CALayer 類延伸了鍵值編碼約定來支持自定義屬性。你可使用該行爲來給圖層添加數據,而後使用你自定義的鍵來獲取。你甚至能夠給你的自定義屬性關聯事件,以致於當你改變屬性時,能夠執行響應的動畫。
有關如何設置和獲取自定義屬性的信息,請參見符合鍵值編碼的容器類。有關向圖層對象添加動做的信息,請參見改變圖層的默認行爲。
在打印過程當中,圖層會根據須要從新繪製其內容,以適應打印環境。Core Animation 一般在渲染到屏幕上時依賴於緩存的位圖,但在打印時會重繪該內容。特別是,若是一個圖層支持視圖使用 drawRect:
方法來提供圖層內容,Core Animation 在打印過程當中再次調用 drawRect:
來生成打印的圖層內容。