在這一章中拟淮,我們將要看一看圖層內(nèi)部是如何根據(jù)父圖層和兄弟圖層來(lái)控制位置和
尺寸的侣灶。另外我們也會(huì)涉及如何管理圖層的幾何結(jié)構(gòu)才漆,以及它是如何被自動(dòng)調(diào)整和
自動(dòng)布局影響的乾胶。
布局
UIView
有三個(gè)比較重要的布局屬性:bounds
,frame
和 center
谴轮, CALayer 對(duì)應(yīng)地叫做 frame
, bounds
和 position
炒瘟。為了能清楚區(qū)分,圖層用了“position
”第步,視圖用了“center
”疮装,但是他們都代表同樣的值。
frame
代表了圖層的外部坐標(biāo)(也就是在父圖層上占據(jù)的空間)雌续, bounds
是內(nèi)部坐標(biāo)({0, 0}
通常是圖層的左上角)斩个,center
和 position
都代表了相對(duì)于 父圖層anchorPoint(錨點(diǎn))
所在的位置。anchorPoint(錨點(diǎn))
的屬性將會(huì)在后續(xù)介紹到驯杜,現(xiàn)在把它想成圖層的中心點(diǎn)就好了受啥。
視圖的frame
, bounds
和 center
屬性僅僅是存取方法鸽心,當(dāng)操縱視圖的frame
滚局,實(shí)際上是在改變位于視圖下方 CALayer
的frame
,不能夠獨(dú)立于圖層之外改變視圖的frame
顽频。
對(duì)于視圖或者圖層來(lái)說(shuō)藤肢, frame
并不是一個(gè)非常清晰的屬性,它其實(shí)是一個(gè)虛擬屬性糯景,是根據(jù) bounds
嘁圈,position
和 transform
計(jì)算而來(lái),所以當(dāng)其中任何一個(gè)值發(fā)生改變蟀淮,frame
都會(huì)變化。相反怠惶,改變frame
的值同樣會(huì)影響到他們當(dāng)中的值
記住當(dāng)對(duì)圖層做變換的時(shí)候,比如旋轉(zhuǎn)或者縮放脓魏,frame
實(shí)際上代表了覆蓋在 圖層旋轉(zhuǎn)之后的整個(gè)軸對(duì)齊的矩形區(qū)域,也就是說(shuō)frame
的寬高可能和bounds
的寬高不再一致了
錨點(diǎn)
之前提到過(guò)通惫,視圖的center
屬性和圖層的position
屬性都指定了anchorPoint
相對(duì)于父圖層的位置茂翔。圖層的anchorPoint
通過(guò) position
來(lái)控制它的frame
的位置,你可以認(rèn)為anchorPoint
是用來(lái)移動(dòng)圖層的把柄履腋。
默認(rèn)來(lái)說(shuō),anchorPoint
位于圖層的中點(diǎn),所以圖層的將會(huì)以這個(gè)點(diǎn)為中心放置.anchorPoint
屬性并沒(méi)有被UIView
接口暴露出來(lái),這也是視圖的position
屬性被叫做“center
”的原因俐末。但是圖層的anchorPoint
可以被移動(dòng),比如你可以把 它置于圖層 frame
的左上角奄侠,于是圖層的內(nèi)容將會(huì)向右下角的 position
方向移動(dòng)卓箫,而不是居中了垄潮。
和前面提到的contentsRect
和 contentsCenter
屬性類(lèi)似, anchorPoint
用單位坐標(biāo)來(lái)描述旅急,也就是圖層的相對(duì)坐標(biāo)牡整,圖層左上角是{0, 0}
,右下角是{1, 1}
逃贝,因此默認(rèn)坐標(biāo)是{0.5, 0.5}
。 anchorPoint
可以通過(guò)指定x
和y
值小于0
或者大于1
沐扳,使它放置在圖層范圍之外。
從上圖可知:當(dāng)改變了 anchorPoint
躯嫉, position
屬性保持固定的值并沒(méi) 有發(fā)生改變杨拐,但是 frame
卻移動(dòng)了。
坐標(biāo)系
和視圖一樣戏阅,圖層在圖層樹(shù)當(dāng)中也是相對(duì)于父圖層按層級(jí)關(guān)系放置,一個(gè)圖層的position
依賴于它父圖層的bounds
奕筐,如果父圖層發(fā)生了移動(dòng),它的所有子圖層也會(huì)跟著移動(dòng)芭逝。
這樣對(duì)于放置圖層會(huì)更加方便渊胸,因?yàn)槟憧梢酝ㄟ^(guò)移動(dòng)根圖層來(lái)將它的子圖層作為一個(gè)整體來(lái)移動(dòng),但是有時(shí)候你需要知道一個(gè)圖層的絕對(duì)位置,或者是相對(duì)于另一個(gè)圖層的位置胖翰,而不是它當(dāng)前父圖層的位置接剩。
CALayer
給不同坐標(biāo)系之間的圖層轉(zhuǎn)換提供了一些工具類(lèi)方法:
- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;
- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;
- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
這些方法可以把定義在一個(gè)圖層坐標(biāo)系下的點(diǎn)或者矩形轉(zhuǎn)換成另一個(gè)圖層坐標(biāo)系下 的點(diǎn)或者矩形.
翻轉(zhuǎn)的幾何結(jié)構(gòu)
常規(guī)說(shuō)來(lái),在iOS
上萨咳,一個(gè)圖層的position
位于父圖層的左上角,但是在Mac OS
上培他,通常是位于左下角。Core Animation
可以通過(guò) geometryFlipped
屬性來(lái)適配這兩種情況俊扳,它決定了一個(gè)圖層的坐標(biāo)是否相對(duì)于父圖層垂直翻轉(zhuǎn)猛遍,是一個(gè) BOOL
類(lèi)型。在iOS
上通過(guò)設(shè)置它為 YES
意味著它的子圖層將會(huì)被垂直翻轉(zhuǎn)抗果, 也就是將會(huì)沿著底部排版而不是通常的頂部(它的所有子圖層也同理奸晴,除非把它們 的 geometryFlipped
屬性也設(shè)為YES
)。
Z坐標(biāo)軸
和UIView
嚴(yán)格的二維坐標(biāo)系不同寄啼,CALayer
存在于一個(gè)三維空間當(dāng)中。除了我們已經(jīng)討論過(guò)的position
和anchorPoint
屬性之外涕刚, CALayer
還有另外兩個(gè)屬性乙帮,zPosition
和 anchorPointZ
,二者都是在Z軸上描述圖層位置的浮點(diǎn)類(lèi)型察净。
注意這里并沒(méi)有更深的屬性來(lái)描述由寬和高做成的 bounds
了,圖層是一個(gè)完全扁平的對(duì)象锈至,你可以把它們想象成類(lèi)似于一頁(yè)二維的堅(jiān)硬的紙片译秦,用膠水粘成一個(gè) 空洞击碗,就像三維結(jié)構(gòu)的折紙一樣们拙。
zPosition
屬性在大多數(shù)情況下其實(shí)并不常用。在第五章晰房,我們將會(huì)涉及 CATransform3D
射沟,你會(huì)知道如何在三維空間移動(dòng)和旋轉(zhuǎn)圖層与境,除了做變換之 外,zPosition
最實(shí)用的功能就是改變圖層的顯示順序了摔刁。
通常,圖層是根據(jù)它們子圖層的 sublayers
出現(xiàn)的順序來(lái)類(lèi)繪制的绑谣,這就是所謂的畫(huà)家的算法--就像一個(gè)畫(huà)家在墻上作畫(huà)--后被繪制上的圖層將會(huì)遮蓋住之前的圖層拗引,但是通過(guò)增加圖層的zPosition
,就可以把圖層向相機(jī)方向前置矾削,于是它就在所有其他圖層的前面了(或者至少是小于它的 zPosition
值的圖層的前面)。
這里所謂的“相機(jī)”實(shí)際上是相對(duì)于用戶是視角哼凯,這里和iPhone
背后的內(nèi)置相機(jī)沒(méi) 任何關(guān)系。
當(dāng)然0.1
或者0.0001
也能夠做到猎贴,但是最好不要這樣蝴光,因?yàn)楦↑c(diǎn)類(lèi)型四舍五 入的計(jì)算可能會(huì)造成一些不便的麻煩。
Hit Testing
CALayer
并不關(guān)心任何響應(yīng)鏈?zhǔn)录锹睿圆荒苤苯犹幚碛|摸事件或者手勢(shì)做瞪。但是 它有一系列的方法幫你處理事件: -containsPoint:
和-hitTest:
右冻。
-containsPoint:
接受一個(gè)在本圖層坐標(biāo)系下的CGPoint
著拭,如果這個(gè)點(diǎn)在圖層frame
范圍內(nèi)就返回 YES
。
-hitTest:
方法同樣接受一個(gè) CGPoint
類(lèi)型參數(shù)伍伤,而不是BOOL
類(lèi)型,它返回圖層本身鄙币,或者包含這個(gè)坐標(biāo)點(diǎn)的葉子節(jié)點(diǎn)圖層。這意味著不再需要像使用 - containsPoint: 那樣因惭,人工地在每個(gè)子圖層變換或者測(cè)試點(diǎn)擊的坐標(biāo)绩衷。如果這個(gè)點(diǎn)在最外面圖層的范圍之外,則返回nil勿决。具體使用 -hitTest:
方法被點(diǎn)擊圖層的 代碼如所示招盲。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//get touch position
CGPoint point = [[touches anyObject] locationInView:self.view];
//get touched layer
CALayer *layer = [self.layerView.layer hitTest:point];
//get layer using hitTest
if (layer == self.blueLayer) {
[[[UIAlertView alloc] initWithTitle:@"Inside Blue Layer"
message:nil
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
} else if (layer == self.layerView.layer) {
[[[UIAlertView alloc] initWithTitle:@"Inside White Layer"
message:nil
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
} }
注意當(dāng)調(diào)用圖層的-hitTest:
方法時(shí),測(cè)算的順序嚴(yán)格依賴于圖層樹(shù)當(dāng)中的圖層順序(和 UIView 處理事件類(lèi)似)表制。之前提到的 zPosition 屬性可以明顯改變屏幕 上圖層的順序控乾,但不能改變事件傳遞的順序。
這意味著如果改變了圖層的z
軸順序壤短,你會(huì)發(fā)現(xiàn)將不能夠檢測(cè)到最前方的視圖點(diǎn)擊事件慨仿,這是因?yàn)楸涣硪粋€(gè)圖層遮蓋住了,雖然它的zPosition
值較小镰吆,但是在圖層樹(shù)中的順序靠前。
自動(dòng)布局
你可能用過(guò)UIViewAutoresizingMask
類(lèi)型的一些常量,應(yīng)用于當(dāng)父視圖改尺寸的時(shí)候摧找,相應(yīng)UIView
的 frame
也跟著更新的場(chǎng)景(通常用于橫豎屏切換)。
當(dāng)使用視圖的時(shí)候芝雪,可以充分利用UIView
類(lèi)接口暴露出來(lái)的UIViewAutoresizingMask
和 NSLayoutConstraint API
,但如果想隨意控制CALayer
的布局综苔,就需要手工操作。最簡(jiǎn)單的方法就是使用CALayerDelegate
如下函數(shù):
- (void)layoutSublayersOfLayer:(CALayer *)layer;
當(dāng)圖層的bounds
發(fā)生改變如筛,或者圖層的-setNeedsLayout
方法被調(diào)用的時(shí) 候杨刨,這個(gè)函數(shù)將會(huì)被執(zhí)行。這使得你可以手動(dòng)地重新擺放或者重新調(diào)整子圖層的大小拭嫁,但是不能像 UIView
的autoresizingMask
和 constraints
屬性做到自適 應(yīng)屏幕旋轉(zhuǎn)抓于。
這也是為什么最好使用視圖而不是單獨(dú)的圖層來(lái)構(gòu)建應(yīng)用程序的另一個(gè)重要原因
之一。
總結(jié)
本章涉及了CALayer
的集合結(jié)構(gòu)怕品,包括它的 frame
巾遭,position
和 bounds
,介紹了三維空間內(nèi)圖層的概念吼和,以及如何在獨(dú)立的圖層內(nèi)響應(yīng)事件骑素,最后簡(jiǎn)單說(shuō)明了在iOS
平臺(tái),Core Animation
對(duì)自動(dòng)調(diào)整 和自動(dòng)布局支持的缺乏末捣。