[譯] Core Animation 編程指南 - 構建圖層層級結構

大多數時間裏,使用圖層的最好方式就是和視圖對象一塊兒使用。然而,有時你可能須要經過向視圖中添加額外的圖層對象來加強視圖層次結構。當圖層可以提供更好的性能或者僅用視圖很難實現的特性時,你可能會使用圖層。在這些狀況下,你須要知道如何管理你建立的圖層的層級結構。html

重要:在 OS X v10.8 及之後的版本,推薦儘量少的使用圖層層級結構,儘可能使用圖層支持視圖。該版本引進的圖層重繪協議容許你自定義圖層支持視圖的行爲,而且仍然能得到以前單獨使用圖層時的性能。數組

將圖層分配到圖層層級結構中

圖層層級結構在不少方面都和視圖層級結構類似。你能夠將一個圖層嵌入到另外一個圖層中,給兩個圖層建立父-子關係。嵌入的圖層稱爲子圖層(sublayer),被嵌入的圖層稱爲父圖層(superlayer)。這種父子關係影響子圖層的多個方面。例如,它的內容在父圖層的內容上面,它的位置與它父圖層的座標系相關,應用在父圖層上的任何形變也會影響子圖層。bash

添加、插入、移除子圖層

每一個圖層對象都有添加、插入、移除子圖層的對象方法。表 4-1 概述了這些方法和它們的行爲。app

表 4-1 修改圖層層級結構的方法ide

行爲 方法 描述
添加圖層 addSublayer: 在當前圖層上添加一個新的子圖層對象。子圖層被添加到當前圖層的子圖層列表的尾部。這將致使子圖層出如今 zPosition 屬性中具備相同值的任何同級圖層的頂部。
插入圖層 insertSublayer:above: insertSublayer:atIndex: insertSublayer:below: 將子圖層添加到具體索引的位置或者與另外一個子圖層相關的位置。當在另外一個子圖層上面或者下面插入時,你只是指定了子圖層在子圖層數組中的位置。圖層實際的視覺效果主要由它們的 zPosition 屬性的值來決定,其次才由它們在子圖層數組的位置決定。
移除圖層 removeFromSuperlayer 在父圖層上移除子圖層。
替換圖層 replaceSublayer:with: 將子圖層替換爲另外一個。若是你插入的子視圖已經添加在別的圖層層級結構上,它會先從那個層級結構移除。

你能夠在本身建立圖層對象時使用上述方法。你不該該使用上述方法來分配屬於圖層支持視圖的圖層。但,圖層支持視圖能夠當作你本身建立的單獨圖層的父圖層。佈局

子圖層位置和尺寸的調整

當添加和插入子圖層時,在圖層顯示在屏幕上以前,你必須設置圖層的位置和尺寸。在將圖層添加到圖層層級結構以後,你也能夠修改子圖層的位置和尺寸。但你應該養成在建立圖層時就指定圖層的位置和尺寸的值得習慣。post

你可使用 bounds 屬性來設置子圖層的尺寸,使用 position 屬性來設置它在父圖層中的位置。矩形邊界的起點永遠是(0, 0),尺寸則是你給圖層以點爲單位的任何尺寸。position 屬性的值是相對於圖層的錨點來定義的,它默認是圖層的中心點。若是你不給這些屬性賦值,Core Animation 會將圖層的寬高初始值設爲0,位置的初始值設爲(0, 0)。性能

myLayer.bounds = CGRectMake(0, 0, 100, 100);
myLayer.position = CGPointMake(200, 200);
複製代碼

重要:始終使用整數表示圖層的寬度和高度。動畫

圖層層級結構如何影響動畫

一些父圖層的屬性能夠影響應用在子圖層上的任何動畫的行爲。其中一個屬性就是 speed,它是動畫速度的乘數。該屬性的值默認爲1.0,但將它改成2.0會致使動畫按起始速度的兩倍速來運行,結果就是會提早一半的時間結束。該屬性不只影響修改的這一個圖層,它也會影響它的子圖層。子圖層的速度也會加倍。若是父圖層和子圖層的速度都爲2.0,那子圖層上的動畫會以原始速度的4倍速度來執行。ui

大多數其餘圖層更改會以可預測的方式影響任何包含的子圖層。例如,對圖層應用一個旋轉形變會致使該圖層的全部子圖層都會旋轉。相似的,改變圖層的透明度也會影響子圖層的透明度。圖層大小的更改遵循的規則,請參見調整圖層層級結構的佈局

調整圖層層級結構的佈局

Core Animation 支持多個選項,用來調整子圖層的位置和尺寸以響應父圖層的改變。在 iOS,圖層支持視圖的廣泛使用使得圖層層次結構的建立變得不那麼重要;僅支持手動佈局更新。在 OS X,其餘的可用選項會使圖層層級管理變得更加容易。

圖層級佈局僅與你使用你建立的單獨圖層對象來構建圖層層級結構時相關。若是你的 app 圖層都是和視圖聯合的,使用基於視圖的佈局支持去更新視圖的尺寸和位置來響應改變。

在 OS X 上使用約束去管理你的圖層層級結構

約束是你使用圖層和父圖層或同級圖層的一系列詳細的關係來制定圖層的尺寸和位置。定義約束需遵循一下步驟:

  1. 建立一個或多個 CAConstraint 對象。使用這些對象去定義約束參數。
  2. 將約束對象添加到屬性修改的圖層上。
  3. 獲取共享的 CAConstraintLayoutManager 對象,將它賦值給最近的父圖層。

圖4-1 展現了你可使用去定義約束的屬性,和它影響了圖層的方面。你可使用約束,根據中點邊緣相對於另外一圖層的位置來更改圖層的位置。你也可使用它們改變圖層的尺寸。你所作的改變能夠與父圖層成比例,或者和同級圖層成比例。你甚至能夠將縮放因子或常量添加到結果變化中。這種額外的靈活性使得使用一組簡單的規則,便可很是精確地控制層的大小和位置。

圖 4-1 約束佈局管理屬性

每一個約束對象包含兩個圖層在同一軸線上的幾何關係。每一個軸最多能夠分配兩個約束對象,正是這兩個約束決定了哪一個屬性是可變的。例如,若是爲圖層的左邊緣和右邊緣指定約束,圖層的大小會發生變化。若是爲圖層的左邊緣和寬度指定約束,圖層右邊緣的位置會發生變化。若是爲圖層的一條邊指定單個約束,Core Animation 會建立一個隱式約束,使圖層的大小保持固定在給定的尺寸中。

當建立約束時,你必須始終指明這三方面的信息:

  • 要約束的圖層的哪一個方面
  • 用哪一個圖層看成參考圖層
  • 在比較中使用的參考圖層的哪一個方面

例4-1 展現了一個約束的例子,該約束表示圖層的垂直中心點與父圖層的垂直中心點相等。當用父視圖作參考圖層時,使用 superlayer 字符串。使用它不須要有指向該圖層的指針或知道該圖層的名稱。它也容許你改變父圖層,若是你改變父圖層,約束會自動應用到新的父圖層上。(當建立於同級圖層相關聯的約束時,你必須使用它的 name 屬性來識別同級圖層。)

例 4-1 定義簡單的約束

[myLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidY]];
複製代碼

在運行時應用約束,你必須將共享的 CAConstraintLayoutManager 對象附加給最近的父圖層。每一個圖層有責任去管理子圖層的佈局。將佈局管理器分配給父級會告訴 Core Animation 應用其子級定義的約束。佈局管理對象自動應用約束。將它賦值給父圖層以後,你就沒必要告訴它去更新佈局。

看圖 4-2,去了解約束是如何工做的。在該例中,設計要求 layerA 的寬高保持不變,而且 layerA 要位於圖圖層的中心點。另外, layerB 的寬度必須和 layerA 的寬度相等,layerB 的上邊界要距離 layerA 的下邊界 10 個點,layerB 的下邊界要比父圖層的下邊界高 10 個點。例 4-2 展現了該例中的代碼是如何實現的。

圖 4-2 基於佈局的約束示例

例 4-2 設置圖層的約束

// 建立設置父圖層的佈局管理
theLayer.layoutManager = [CAConstraintLayoutManager layoutManager];
 
// 建立第一個子圖層
CALayer *layerA = [CALayer layer];
layerA.name = @"layerA";
layerA.bounds = CGRectMake(0.0,0.0,100.0,25.0);
layerA.borderWidth = 2.0;
 
// 保持 layerA 的中點與父圖層的中點相同
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidY]];
[layerA addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMidX]];
[theLayer addSublayer:layerA];
 
// 建立第二個子圖層
CALayer *layerB = [CALayer layer];
layerB.name = @"layerB";
layerB.borderWidth = 2.0;
 
// 使 layerB 的寬度與 layerA 的相同
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintWidth
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintWidth]];
 
//  layerB 和 layerA 水平居中
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintMidX]];
 
// layerB 的上邊界距 layerA 的下邊界 10 個點
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY
                                                 relativeTo:@"layerA"
                                                  attribute:kCAConstraintMinY
                                                     offset:-10.0]];
 
// `layerB` 的下邊界比父圖層的下邊界高 10 個點
[layerB addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMinY
                                                 relativeTo:@"superlayer"
                                                  attribute:kCAConstraintMinY
                                                     offset:+10.0]];
 
[theLayer addSublayer:layerB];
複製代碼

上述代碼中有一個有意思的事,它沒有顯式的設置 layerB 的尺寸。因爲定義了約束,每次佈局更新,layerB 的寬高都會自動設置。所以,再設置它的寬高是不必的。

警告:當建立約束時,不要在約束中建立循環引用。循環約束使得計算所需的佈局信息變得不可能。當遇到這種循環引用時,佈局行爲是未定義的。

OS X 圖層層級結構設置自動調整尺寸規則

在 OS X 中,自動調整尺寸規則是另外一種調整圖層尺寸和位置的方法。使用自動調整尺寸規則,你能夠指定圖層的邊緣是應該與父圖層的相應邊緣保持固定距離仍是可變距離。相似的,你也能夠指定圖層的寬高是固定的仍是可變的。指定的關係始終是關於圖層和其父圖層的。你不能將自動調整尺寸規則應用於同級圖層。

設置圖層的自動調整尺寸規則,你必須給圖層的 autoresizingMask 屬性賦值合適的常量。圖層默認是有固定寬高的。在佈局期間,圖層精確的尺寸和位置是由 Core Animation 自動爲你計算的,這是包含基於不少因素的一系列複雜計算。Core Animation 在你的代理執行任何手動佈局更新以前應用自動調整尺寸行爲,因此,你能夠在須要的時候使用代理去微調自動調整尺寸佈局的結果。

手動佈局你的圖層層次結構

在 iOS 和 OS X 上,你能夠經過實現父圖層的代理對象的 layoutSublayersOfLayer: 方法來手動處理佈局。你可使用該方法調整圖層中任意子圖層的尺寸和位置。當進行手動佈局更新時,由你來執行必要的計算來定位每一個子層

若是你實現一個自定義的圖層子類,你的子類能夠重寫 layoutSublayers 方法,使用該方法(不是代理)去處理任何佈局任務。你應該僅在你想完整的控制你自定義圖層類的子圖層的位置時,才重寫此方法。替換默認的實現會阻止 Core Animation 在 OS X 上應用約束或自動調整尺寸規則。

子圖層和剪切

不想視圖,父圖層不會自動剪切子圖層的內容,這會致使子圖層內容會延伸到父圖層外面。換言之,父圖層默認容許子圖層完整的展現它的內容。然而,你能夠經過將圖層的 masksToBounds 屬性設置爲 YES 來將剪切功能生效,

圖層的剪切蒙版的形狀包含圖層的圓角,若是指定圓角的話。圖 4-3 展現了圖層 masksToBounds 屬性如何影響圖層圓角。當此屬性設置爲 NO 時,子類會完整的顯示其內容,即便它們內容超出了父圖層的邊界。將此屬性的值改成 YES 會致使它們的內容被剪切。

圖 4-3 剪切超出父圖層邊界的子圖層的內容

轉換圖層之間的座標值

有時,你可能須要將一個圖層的座標值轉換到同屏幕上不一樣圖層上的座標值。CALayer 類提供一系列簡單的轉換方法來達到你的目的:

  • convertPoint:fromLayer:
  • convertPoint:toLayer:
  • convertRect:fromLayer:
  • convertRect:toLayer:

除了轉換點和矩形值以外,你也能夠經過 convertTime:fromLayer:convertTime:toLayer: 方法來轉換兩個圖層之間的時間值。每一個圖層都定義本身的時空,使用本身的時空去同步動畫的開始和結束時間和系統的其他部分。時空默認是同步的;可是,若是你改變一系列圖層的動畫速度,這些圖層的時空也會相應改變。你可使用時間轉換方法來考慮任何此類因素,來確保兩層的時間同步。

最後

相關文章
相關標籤/搜索