2. The Backing Image
A picture is worth a thousand words. An interface is worth a thousand pictures.
——Ben Shneiderman
CALayer 有一個(gè) id 類型的屬性 contents吩抓,但是實(shí)際上伦连,只有在你傳入一個(gè) CGImage 時(shí)才能起作用扔枫。其 id 類型的原因在于當(dāng)它在 Mac OS 上被使用時(shí),你既可以賦予這個(gè)屬性一個(gè) CGImage 或者 NSImage 痕貌,但是在 iOS 上入宦,你只能賦一個(gè) CGImage 給它,像 UIImage 這樣的也不行轿钠。
實(shí)際使用 CALayer 的 contents 屬性時(shí),你需要提供的是一個(gè) CGImageRef,也就是一個(gè)指向 CGImage 結(jié)構(gòu)體的指針芽世。UIImage 有一個(gè)叫做 CGImage 的屬性妹卿,這個(gè)屬性會(huì)返回一個(gè)隱含的 CGImageRef箕宙,如果你直接把這個(gè)值賦給 CALayer 的 contents 屬性狡门,編譯時(shí)是會(huì)報(bào)錯(cuò)的,因?yàn)?CGImageRef 不是一個(gè) Cocoa 對(duì)象,它是 Core Foundation 類型的數(shù)據(jù)肤寝。所以义桂,這里我們需要用橋接(bridge cast)的方式來(lái)將其轉(zhuǎn)換成 id 類型的數(shù)據(jù):
layer.contents = (__bridge id)image.CGImage;
盡管我們可以通過(guò)設(shè)置 CALyer 的 contents 屬性來(lái)展示圖片曹抬,但是它并不是像 UIImageView 那樣專門用來(lái)展示圖片的疾宏。
contentsGravity 屬性:類似于 UIView 的contentMode 屬性岩馍,決定內(nèi)容的展示位置和尺寸比例蛀恩,它是一個(gè) NSString 類型的值,我們可以從系統(tǒng)的 framework 中定義的字符串常量選用自己想要的值。
contentScale 屬性:這個(gè)屬性定義了 layer 中圖片(contents)的像素尺寸與 view 的尺寸的比例,默認(rèn)值為 1.0庞呕,也就是說(shuō)在屏幕繪制該圖形時(shí)地啰,是按照一點(diǎn)(point)剛好就是1個(gè)像素(pixel)的分辨率來(lái)處理的盏混,如果這個(gè)值為 2.0,那就代表一點(diǎn)中顯示兩個(gè)像素的內(nèi)容(Retina 顯示屏就是這樣的分辨率)。值得注意的是鳖粟,當(dāng)你將 CALayer 的 contentGravity 屬性設(shè)為 KCAGravityResizeAspect 時(shí),contentScale 屬性是不起作用的贞瞒。UIView 也有一個(gè)類似的屬性 contentScaleFactor掰盘,但是我們很少用到。最后,在操作 CALayer 的 contents 時(shí)钢猛,一定要記得手動(dòng)設(shè)置 layer 的 contentsScale 屬性來(lái)適配屏幕分辨率:
layer.contentScale = [UIScreen mainScreen].scale;
maskToBounds 屬性:類似于 UIView 的 clipsToBounds 屬性,用來(lái)裁剪超出 frame 邊界的內(nèi)容命迈。
contentsRect 屬性:用來(lái)在 layer 中展示圖片的一部分內(nèi)容。不像 bounds 和 frame 這些以點(diǎn)(point)為單位,contentsRect 以單元坐標(biāo)(unit coordinates)為單位勃黍,取值范圍從 0 到 1弄息,是一個(gè)相對(duì)值。contentsRect 屬性默認(rèn)值為{0, 0, 1, 1}粗恢,也就是說(shuō)圖片正好完整地顯示在 layer 的 frame 中柑晒。
Image Sprites :對(duì) contentsRect 屬性最有意思的應(yīng)用就是能夠使用 image sprites 了。Sprites 一般用在像 Cocos2D 這樣的 2D 游戲引擎中眷射,通過(guò) OpenGL 來(lái)顯示圖片匙赞。作者 Nick Lockwood 還寫了一個(gè)關(guān)于 Sprites 的開(kāi)源庫(kù):https://github.com/nicklockwood/LayerSprites佛掖。
contentCenter 屬性:類似于 UIImage 的 -resizableImageWithCapInsets:方法一樣,確定一塊可伸縮的中間區(qū)域涌庭,在 layer 大小發(fā)生改變時(shí)芥被,中間區(qū)域(contentCenter)自動(dòng)伸縮,而四周不變坐榆。默認(rèn)值是{0, 0, 1, 1}拴魄。
UIView 默認(rèn)沒(méi)有實(shí)現(xiàn) -drawRect:方法,因?yàn)槿绻?UIView 只是填充了某種顏色或者它的 layer 的 contents 已經(jīng)包含了一個(gè) image 實(shí)例席镀,這樣的話 UIView 就不需要一個(gè)自定義的 backing image了匹中;當(dāng) UIView 檢測(cè)到 -drawRect:方法被實(shí)現(xiàn)了,系統(tǒng)將會(huì)為這個(gè) view 生成一個(gè) backing image愉昆,這個(gè) backing image 的像素尺寸等于 view 的尺寸乘以 contentScale 职员。如果你不需要這個(gè) backing image,你就最好不要實(shí)現(xiàn) -drawRect:方法跛溉,因?yàn)檫@樣會(huì)浪費(fèi)內(nèi)存和 CPU焊切,這也是 Apple 為什么告訴你如果不想進(jìn)行自定義繪制的話就不要留一個(gè)空的-drawRect:方法在那里的原因。
當(dāng) view 第一次出現(xiàn)在屏幕上時(shí)芳室,-drawRect 方法就被自動(dòng)調(diào)用了专肪,-drawRect:方法中的自定義實(shí)現(xiàn)會(huì)被緩存起來(lái),直到 view 需要更新的時(shí)候堪侯。
盡管 -drawRect:方法是 UIView 的方法嚎尤,但它實(shí)際上是由隱藏在 view 背后的 layer 來(lái)管理繪制和存儲(chǔ)圖片的。CALayer 有一個(gè)遵循 CALayerDelegate 協(xié)議的 delegate 屬性伍宦,當(dāng) CALayer 需要繪制信息時(shí)芽死,它就會(huì)詢問(wèn)它的 delegate。CALayer 在繪制時(shí)會(huì)調(diào)用其 delegate 的兩個(gè)方法:
- (void)displayLayer:(CALayerCALayer *)layer;
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
除非你單獨(dú)用到了 CALyer 來(lái)繪制次洼,大多數(shù)情況下关贵,你根本不要實(shí)現(xiàn) CALayerDelegate 協(xié)議。因?yàn)楫?dāng) UIView 創(chuàng)建了他的附屬 layer 時(shí)卖毁,會(huì)自動(dòng)將把它自己設(shè)為那個(gè) layer 的 delegate揖曾,并且實(shí)現(xiàn)了 -displayLayer:方法。