CoreAnimation圖層的樹狀結構和寄宿圖

1.圖層的樹狀結構

Core Animation的前身叫做Layer Kit,所以,你應該意識到Core Animation並不只是用來做動畫的。做動畫只是Core Animation特性的冰山一角。

Core Animation是一個複合引擎,他的職責就是儘可能的組合屏幕上不同的可視類容,這個內容是被分解成獨立的圖層,存儲在一個叫做圖層樹的體系之中。

這個樹形成了UIKit以及在iOS應用程序中你所能在屏幕上看見的一切的基礎。

視圖:

一個視圖就是屏幕上顯示的一個矩形塊(比如圖片,文字或者視頻),它能夠攔截類似鼠標點擊或者觸摸手勢等用戶輸入。視圖的層級關係中可以互相嵌套,一個視圖可以管理它的所有子視圖

在iOS中,所有的視圖都從一個叫做UIView的基類派生而來,UIView可以處理觸摸事件,可以支持基於Core Graphics繪圖,可以做仿射變換(例如旋轉或者縮放),或者簡單的類似於滑動或者漸變的動畫。

圖層:

CALayer類在概念上和UIView類似,同樣也是一些被層級關係樹管理的矩形塊,同樣也包含一些內容(圖片、文字或者背景色),能管理所有子圖層的位置。圖層有一些方法和屬性來做動畫和變換。和UIView最大的不同是CALayer不處理用戶交互,因爲CALayer並不清楚具體的相應鏈(iOS通過視圖層級關係用來傳送觸摸事件的機制)。但是圖層提供了一些方法來判斷是否一個觸點在圖層的範圍之內。

小結:UIView可以說就是CALayer的代理,視圖的職責就是創建並管理它對應的圖層,以確保子視圖在層級關係中添加或者被移除的時候,他們關聯的圖層也同樣在對應的層級關係樹當中有相同的操作。

CALayer的能力:

對於一些簡單的需求來說,我們沒必要處理CALayer。因爲蘋果已經通過UIView的高級API間接地使動畫變得很簡單。但是UIView是對CALayer的封裝,這使得UIView的靈活性遠遠低於CALayer。如下是UIView沒有暴露出來的CALayer的功能:

1.陰影、圓角、帶顏色的邊框

2.3D變換

3.非矩陣範圍

4.透明遮罩

5.多級非線性動畫

說明:使用圖層需要導入QuartzCore框架。我們一般的情況下添加UIView的時候附帶添加了其對應的CALayer。我們也可以只在UIView上添加CALayer。

某些特殊情況下更需要使用CALayer:

1.開發同時可以在Mac OS上運行的跨平臺應用

2.使用多種CALayer的子類,並且不想創建UIView去包裝它們

3.做一些對性能特別挑剔的工作,比如對UIView一些可以忽略不計的操作都會引起顯著的不同。

---------------------------

2.寄宿圖(CALayer包含的圖)

屬性:

contents:

該屬性被定義爲id,但是隻能給它賦CGImageRef類型的值,因爲跨平臺的問題(在Mac OS系統上,這個屬性對CGImage和NSImage類型的值都有用)。UIImage有個CGImage屬性類型是CGImageRef。但是CGImageRef並不是一個Cocoa對象,而是一個Core Foundation類型,所以必須使用橋接來賦值。

eg:  layer.contents = (_bridge id)image.CGImage;

------------

contentGravity:

其實這個屬性對應的就是UIView裏面的contentMode屬性。用法差不多,contentMode對應着一個枚舉,contentGravity對應着NSString。

eg:  self.layerView.layer.contentsGravity = kCAGravityResizeAspect;對應着aspectFit

說明:kCAGravityResizeAspect並不會考慮分辨率的問題,它就是拉伸圖片以適應圖層而已。

------------

contentScale:

這個屬性並不是一直有用,比如將contentGravity設置爲了kCAGravityResizeAspect,那麼設置contentScale屬性是沒用的。但是如果你設置其爲kCAGravityCenter(不會拉伸圖片),那就有效果了。

contentScale屬性屬於支持高分辨率屏幕機制的一部分。如果設置爲1那麼代表每個點一個像素來繪製圖片,如果設置爲2那麼就代表每個點2*2個像素來繪製圖片。

相比較於UIImage,CGImage沒有拉伸的概念。當你設置kCAGravityResizeAspect的時候其實就是強行拉伸了,所以設置contentScale沒有用。所以使用CGImage來設置圖層的內容,並且不拉伸的時候,一般帶上這句話:

eg:  

self.layerView.layer.contentsScale = image.scale;

或者

self.layerView.layer.contentsScale =

[UIScreen mainScreen].scale

;

--------------

maskToBounds:

對比到UIView就是clipsToBounds屬性。決定超出邊界的內容是否顯示。

---------------

contentRect:

CALayer的contentRect屬性容許我們顯示圖層框內的寄宿圖的局部,這涉及到圖片是如何顯示和拉伸的,所以要比contentGravity靈活很多。和bounds、frame不同,contentRect採用了單位座標(值在0~1),是一個相對值(像素和點都是絕對值)。所以contentRect是相對於寄宿圖的尺寸的。

eg: layer.contentRect = CGRectMake(0, 0, 0,5, 0.5);


iOS使用的座標系統:

1.點--在iOS和Mac OS中最常見的座標體系。點就像是虛擬的像素,也被稱作邏輯像素。在標準設備上,一個點就是一個像素,但是在Retina設備上,一個點等於2*2個像素。iOS用點作爲屏幕的座標測算體系就是爲了在Retina設備和普通設備上能有一致的視覺效果。

2.像素 —— 物理像素座標並不會用來屏幕布局,但是仍然與圖片有相對關係。UIImage是一個屏幕分辨率解決方案,所以指定點來度量大小。但是一些底層的圖片表示如CGImage就會使用像素,所以你要清楚在Retina設備和普通設備上,他們表現出來了不同的大小。

3.單位 —— 對於與圖片大小或是圖層邊界相關的顯示,單位座標是一個方便的度量方式, 當大小改變的時候,也不需要再次調整。單位座標在OpenGL這種紋理座標系統中用得很多,Core Animation中也用到了單位座標。

關於contentRect屬性的一個很有意思的用法--(圖片拼接)

規則:像平常一樣載入我們的大圖,然後把它賦值給四個獨立的圖層的contents,然後設置每個圖層的contentsRect來去掉我們不想顯示的部分


- (void)addSpriteImage:(UIImage *)image withContentRect:(CGRect)rect toLayer:(CALayer *)layer  {
<pre name="code" class="objc">//set image


layer.contents = (__bridge id)image.CGImage;
<pre name="code" class="objc">//scale contents to fit

 layer.contentsGravity = kCAGravityResizeAspect;
//set contentsRect 
layer.contentsRect = rect;
}


- (void)viewDidLoad 
{
  [super viewDidLoad]; //load sprite sheet
  UIImage *image = [UIImage imageNamed:@"Sprites.png"];//加載拼接圖片
  //set igloo sprite
  [self addSpriteImage:image withContentRect:CGRectMake(0, 0, 0.5, 0.5) toLayer:self.iglooView.layer];//gei
  //set cone sprite
  [self addSpriteImage:image withContentRect:CGRectMake(0.5, 0, 0.5, 0.5) toLayer:self.coneView.layer];
  //set anchor sprite
  [self addSpriteImage:image withContentRect:CGRectMake(0, 0.5, 0.5, 0.5) toLayer:self.anchorView.layer];
  //set spaceship sprite
  [self addSpriteImage:image withContentRect:CGRectMake(0.5, 0.5, 0.5, 0.5) toLayer:self.shipView.layer];
}



拼合不僅給app提供了一個整潔的載入方式,還有效地提高了載入性能(單張大圖比多張小圖載入地更快),但是如果有手動安排的話,他們還是有一些不方便的,如果你需要在一個已經創建好的品和圖上做一些尺寸上的修改或者其他變動,無疑是比較麻煩的。

Mac上有一些商業軟件可以爲你自動拼合圖片,這些工具自動生成一個包含拼合後的座標的XML或者plist文件,拼合圖片的使用大大簡化。這個文件可以和圖片一同載入,並給每個拼合的圖層設置contentsRect,這樣開發者就不用手動寫代碼來擺放位置了。

這些文件通常在OpenGL遊戲中使用,不過呢,你要是有興趣在一些常見的app中使用拼合技術,那麼一個叫做LayerSprites的開源庫(https://github.com/nicklockwood/LayerSprites),它能夠讀取Cocos2D格式中的拼合圖並在普通的Core Animation層中顯示出來。

------------------------

contentsCenter:

contentsCenter其實是一個Rect,它定義了一個固定的邊框和一個在圖層上可拉伸的區域。改變contentsCenter的值並不會影響到寄宿圖的顯示,除非這個圖層的大小改變了,纔看得到效果。現在一個圖層的大小是100*100,放一張100*100的圖片上去,然後設置contentRect,顯示圖片的局部,但是圖層的大小沒變,此時圖片就有了拉伸,此時再去設置contentsCenter就會有效果,前提是圖片一定是拉伸了。

CGImageRef image = [UIImage imageNamed:@"3"].CGImage;
    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(20, 20, CGImageGetWidth(image), CGImageGetHeight(image));;
    btn.layer.contents = (__bridge id)image;
    btn.layer.contentsRect = CGRectMake(0.25, 0.25, 0.5, 0.5);
    btn.layer.contentsCenter = CGRectMake(0.33, 0.33, 0.33, 0.33);
    btn.layer.contentsGravity = kCAGravityResize;
    self.btn1 = btn;
    [self.view addSubview:self.btn1];

原圖:

下面是局部顯示的原圖,上面是利用contentsCenter處理後的圖片


說明:在SB裏面有個Stretching就是用來設置contetsCenter的。

步驟:在SB上面拖一個Btn,選屬性下面就看到了


















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章