CALayer

CALayer1-簡介

一、什麼是CALayer

* 在iOS系統中,你能看得見摸得着的東西基本上都是UIView,比如一個按鈕、一個文本標籤、一個文本輸入框、一個圖標等等,這些都是UIView。

* 其實UIView之所以能顯示在屏幕上,完全是因爲它內部的一個

* 在創建UIView對象時,UIView內部會自動創建一個(即CALayer對象),通過UIView的layer屬性可以訪問這個層。當UIView需要顯示到屏幕上時,會調用drawRect:方法進行繪圖,並且會將所有內容繪製在自己的上,繪圖完畢後,系統會將拷貝到屏幕上,於是就完成了UIView的顯示。

* 換句話說,UIView本身不具備顯示的功能,是它內部的纔有顯示功能。

 

二、CALayer的簡單使用

上面已經說過了,UIView之所以能夠顯示,完全是因爲內部的CALayer對象。因此,通過操作這個CALayer對象,可以很方便地調整UIView的一些界面屬性,比如:陰影、圓角大小、邊框寬度和顏色等。

1.CALayer是被定義在QuartzCore框架中的,因此要想使用CALayer,先導入QuartzCore框架

1> 點擊項目名稱,然後點擊右邊TARGETS下面的target

2> 點擊Build Pases後,展開Link Binary....,添加 + 號

3> 在搜索框中輸入"Quartz",選中QuartzCore.framework,最後add添加

4> 添加完畢後,這個框架就會出現在項目文件夾中

如果你覺得位置不好看,還可以將它拖到Frameworks文件夾下,跟其他框架放一起

 

2.在項目代碼中導入QuartzCore框架的主頭文件

#import <QuartzCore/QuartzCore.h>

 

3.通過CALayer修改UIImageView的界面屬性

你也可以使用UIButton或者UILabel,這裏就以UIImageView爲例子

1> 先創建一個UIImageView,添加到控制器的view中

1 UIImage *image = [UIImage imageNamed:@"lufy.png"];
2 UIImageView *imageView = [[[UIImageView alloc] initWithImage:image] autorelease];
3 imageView.center = CGPointMake(100, 100);
4 [self.view addSubview:imageView];

2> 設置陰影

1 imageView.layer.shadowColor = [UIColor grayColor].CGColor;
2 imageView.layer.shadowOffset = CGSizeMake(10, 10);
3 imageView.layer.shadowOpacity = 0.5; 

* 第1行設置陰影的顏色爲灰色,注意,這裏使用的是UIColor的CGColor屬性,是一種CGColorRef類型的數據

* 第2行設置陰影的偏移大小,可以看出陰影往原圖的右下角偏移

* 第3行設置陰影的不透明度爲0.5,表示半透明。如果爲1,代表完全不透明。

3> 設置圓角大小

通過layer屬性可以訪問視圖內部的CALayer對象

1 imageView.layer.cornerRadius = 10;
2 imageView.layer.masksToBounds = YES;

* 第1行設置圓角半徑爲10

* 第2行的maskToBounds=YES:可以看做是強制內部的所有子層支持圓角效果,少了這個設置,UIImageView是不會有圓角效果的

* 注意,如果設置了maskToBounds=YES,那將不會有陰影效果

4> 設置邊框寬度和顏色

1 imageView.layer.borderWidth = 5;
2 imageView.layer.borderColor = [UIColor redColor].CGColor;

* 第1行設置邊框寬度爲5

* 第2行設置邊框顏色爲紅色

 

5> 設置旋轉

imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);

* 利用transform屬性可以設置旋轉、縮放等效果

* M_PI_4表示四分之π,順時針旋轉45°

* 後面的(0, 0, 1)表示Z軸這個向量,修改這個向量可以做一些三維旋轉效果,你可以隨便改個值試一下,比如(1, 1, 1)

* 總體的意思是layer會繞着Z軸順時針旋轉45°,也就是在x、y平面進行旋轉



CALayer2-創建新的層

上一講已經說過,UIView內部默認有個CALayer對象(層),通過layer屬性可以訪問這個層。要注意的是,這個默認的層不允許重新創建,但可以往層裏面添加子層
UIView可以通過addSubview:方法添加子視圖,類似地,CALayer可以通過addSublayer:方法添加子層
接下來演示一下如何添加子層:

一、添加一個簡單的圖層

複製代碼
 1 CALayer *myLayer = [CALayer layer];
 2 // 設置層的寬度和高度(100x100)
 3 myLayer.bounds = CGRectMake(0, 0, 100, 100);
 4 // 設置層的位置
 5 myLayer.position = CGPointMake(100, 100);
 6 // 設置層的背景顏色:紅色
 7 myLayer.backgroundColor = [UIColor redColor].CGColor;
 8 // 設置層的圓角半徑爲10
 9 myLayer.cornerRadius = 10;
10 
11 // 添加myLayer到控制器的view的layer中
12 [self.view.layer addSublayer:myLayer];
複製代碼

* 第1行創建了一個自動釋放的CALayer對象,你也可以使用經典的alloc和init方法來創建

* 第12行將創建好的層添加到控制器的view的層中

 

二、添加一個顯示圖片的圖層

複製代碼
 1 CALayer *myLayer = [CALayer layer];
 2 // 設置層的寬度和高度(100x100)
 3 myLayer.bounds = CGRectMake(0, 0, 100, 100);
 4 // 設置層的位置
 5 myLayer.position = CGPointMake(100, 100);
 6 // 設置需要顯示的圖片
 7 myLayer.contents = (id)[UIImage imageNamed:@"lufy.png"].CGImage;
 8 // 設置層的圓角半徑爲10
 9 myLayer.cornerRadius = 10;
10 // 如果設置了圖片,需要設置這個屬性爲YES纔有圓角效果
11 myLayer.masksToBounds = YES;
12 
13 // 添加myLayer到控制器的view的layer中
14 [self.view.layer addSublayer:myLayer];
複製代碼

* 在第7行設置需要顯示的圖片,注意,這裏用的是UIImage的CGImage屬性,是一種CGImageRef類型的數據

 

三、爲什麼CALayer中使用CGColorRef和CGImageRef這2種數據類型,而不用UIColor和UIImage?

* 首先要知道:CALayer是定義在QuartzCore框架中的;CGImageRef、CGColorRef兩種數據類型是定義在CoreGraphics框架中的;UIColor、UIImage是定義在UIKit框架中的

* 其次,QuartzCore框架CoreGraphics框架是可以跨平臺使用的,在iOS和Mac OS X上都能使用,但是UIKit只能在iOS中使用

* 因此,爲了保證可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef

* 不過很多情況下,可以通過UIKit對象的特定方法,得到CoreGraphics對象,比如UIImage的CGImage方法可以返回一個CGImageRef

 

四、UIView和CALayer的選擇

細心的朋友不難發現,其實前面的2個效果不僅可以通過添加層來實現,還可以通過添加UIView來實現。比如,第1個紅色的層可以用一個UIView來實現,第2個顯示圖片的層可以用一個UIImageView來實現。 既然CALayer和UIView都能實現相同的顯示效果,那究竟該選擇誰好呢?

* 其實,對比CALayer,UIView多了一個事件處理的功能。也就是說,CALayer不能處理用戶的觸摸事件,而UIView可以

* 所以,如果顯示出來的東西需要跟用戶進行交互的話,用UIView;如果不需要跟用戶進行交互,用UIView或者CALayer都可以

* 當然,CALayer的性能會高一些,因爲它少了事件處理的功能,更加輕量級

 

五、UIView和CALayer的其他關係

UIView可以通過subviews屬性訪問所有的子視圖,類似地,CALayer也可以通過sublayers屬性訪問所有的子層

UIView可以通過superview屬性訪問父視圖,類似地,CALayer也可以通過superlayer屬性訪問父層

* 下面再看一張UIView和CALayer的關係圖:

如果兩個UIView是父子關係,那麼它們內部的CALayer也是父子關係。




CALayer3-層的屬性

一、隱式動畫屬性

* 在前面幾講中已經提到,每一個UIView內部都默認關聯着一個CALayer,我們可用稱這個Layer爲Root Layer(根層)。所有的非Root Layer也就是手動創建的CALayer對象,都存在着隱式動畫。

* 當對非Root Layer的部分屬性進行相應的修改時,默認會自動產生一些動畫效果,這些屬性稱爲Animatable Properties(可動畫屬性)。

* 列舉幾個常見的Animatable Properties:

  • bounds:用於設置CALayer的寬度和高度。修改這個屬性會產生縮放動畫
  • backgroundColor:用於設置CALayer的背景色。修改這個屬性會產生背景色的漸變動畫
  • position:用於設置CALayer的位置。修改這個屬性會產生平移動畫

比如:假設一開始CALayer的position爲(100, 100),然後在某個時刻修改爲(200, 200),那麼整個CALayer就會在短時間內從(100, 100)這個位置平移到(200, 200)

* 我們也可以從官方文檔中查詢所有的Animatable Properties

1.點擊Window -> Organizer

 

2.在搜索框輸入"animatable"即可

 

二、position和anchorPoint

* position和anchorPoint屬性都是CGPoint類型的

* position可以用來設置CALayer在父層中的位置,它是以父層的左上角爲座標原點(0, 0)

* anchorPoint稱爲"定位點",它決定着CALayer身上的哪個點會在position屬性所指的位置。它的x、y取值範圍都是0~1,默認值爲(0.5, 0.5)

1.創建一個CALayer,添加到控制器的view的layer中

複製代碼
 1 CALayer *myLayer = [CALayer layer];
 2 // 設置層的寬度和高度(100x100)
 3 myLayer.bounds = CGRectMake(0, 0, 100, 100);
 4 // 設置層的位置
 5 myLayer.position = CGPointMake(100, 100);
 6 // 設置層的背景顏色:紅色
 7 myLayer.backgroundColor = [UIColor redColor].CGColor;
 8 
 9 // 添加myLayer到控制器的view的layer中
10 [self.view.layer addSublayer:myLayer];
複製代碼

第5行設置了myLayer的position爲(100, 100),又因爲anchorPoint默認是(0.5, 0.5),所以最後的效果是:myLayer的中點會在父層的(100, 100)位置

注意,藍色線是我自己加上去的,方便大家理解,並不是默認的顯示效果。兩條藍色線的寬度均爲100。

 

2.若將anchorPoint改爲(0, 0),myLayer的左上角會在(100, 100)位置

1 myLayer.anchorPoint = CGPointMake(0, 0);

 

3.將anchorPoint改爲(1, 1),myLayer的右下角會在(100, 100)位置

1 myLayer.anchorPoint = CGPointMake(1, 1);

 

 

4.將anchorPoint改爲(0, 1),myLayer的左下角會在(100, 100)位置

1 myLayer.anchorPoint = CGPointMake(0, 1);

我想,你應該已經明白anchorPoint的用途了吧,它決定着CALayer身上的哪個點會在position所指的位置上。它的x、y取值範圍都是0~1,默認值爲(0.5, 0.5),因此,默認情況下,CALayer的中點會在position所指定的位置上。當anchorPoint爲其他值時,以此類推。




CALayer4-自定義層

自定義層,其實就是在層上繪圖,一共有2種方法,下面詳細介紹一下。

一、自定義層的方法1

方法描述:創建一個CALayer的子類,然後覆蓋drawInContext:方法,使用Quartz2D API進行繪圖

1.創建一個CALayer的子類

 

2.在.m文件中覆蓋drawInContext:方法,在裏面繪圖

複製代碼
 1 @implementation MJLayer
 2 
 3 #pragma mark 繪製一個實心三角形
 4 - (void)drawInContext:(CGContextRef)ctx {
 5     // 設置爲藍色
 6     CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);
 7 
 8     
 9     // 設置起點
10     CGContextMoveToPoint(ctx, 50, 0);
11     // 從(50, 0)連線到(0, 100)
12     CGContextAddLineToPoint(ctx, 0, 100);
13     // 從(0, 100)連線到(100, 100)
14     CGContextAddLineToPoint(ctx, 100, 100);
15     // 合併路徑,連接起點和終點
16     CGContextClosePath(ctx);
17     
18     // 繪製路徑
19     CGContextFillPath(ctx);
20 }
21 
22 @end
複製代碼

 

3.在控制器中添加圖層到屏幕上

複製代碼
1 MJLayer *layer = [MJLayer layer];
2 // 設置層的寬高
3 layer.bounds = CGRectMake(0, 0, 100, 100);
4 // 設置層的位置
5 layer.position = CGPointMake(100, 100);
6 // 開始繪製圖層
7 [layer setNeedsDisplay];
8 [self.view.layer addSublayer:layer];
複製代碼

注意第7行,需要調用setNeedsDisplay這個方法,纔會觸發drawInContext:方法的調用,然後進行繪圖

 

二、自定義層的方法2

方法描述:設置CALayer的delegate,然後讓delegate實現drawLayer:inContext:方法,當CALayer需要繪圖時,會調用delegate的drawLayer:inContext:方法進行繪圖。

* 這裏要注意的是:不能再將某個UIView設置爲CALayer的delegate,因爲UIView對象已經是它內部根層的delegate,再次設置爲其他層的delegate就會出問題。UIView和它內部CALayer的默認關係圖:

1.創建新的層,設置delegate,然後添加到控制器的view的layer中

複製代碼
 1 CALayer *layer = [CALayer layer];
 2 // 設置delegate
 3 layer.delegate = self;
 4 // 設置層的寬高
 5 layer.bounds = CGRectMake(0, 0, 100, 100);
 6 // 設置層的位置
 7 layer.position = CGPointMake(100, 100);
 8 // 開始繪製圖層
 9 [layer setNeedsDisplay];
10 [self.view.layer addSublayer:layer];
複製代碼

* 在第3行設置了CALayer的delegate,這裏的self是指控制器

注意第9行,需要調用setNeedsDisplay這個方法,纔會通知delegate進行繪圖

 

2.讓CALayer的delegate(前面設置的是控制器)實現drawLayer:inContext:方法

複製代碼
 1 #pragma mark 畫一個矩形框
 2 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
 3     // 設置藍色
 4     CGContextSetRGBStrokeColor(ctx, 0, 0, 1, 1);
 5     // 設置邊框寬度
 6     CGContextSetLineWidth(ctx, 10);
 7     
 8     // 添加一個跟層一樣大的矩形到路徑中
 9     CGContextAddRect(ctx, layer.bounds);
10     
11     // 繪製路徑
12     CGContextStrokePath(ctx);
13 } 
複製代碼

 

 三、其他

1.總結

無論採取哪種方法來自定義層,都必須調用CALayer的setNeedsDisplay方法才能正常繪圖。

 

2.UIView的詳細顯示過程

* 當UIView需要顯示時,它內部的層會準備好一個CGContextRef(圖形上下文),然後調用delegate(這裏就是UIView)的drawLayer:inContext:方法,並且傳入已經準備好的CGContextRef對象。而UIView在drawLayer:inContext:方法中又會調用自己的drawRect:方法

* 平時在drawRect:中通過UIGraphicsGetCurrentContext()獲取的就是由層傳入的CGContextRef對象,在drawRect:中完成的所有繪圖都會填入層的CGContextRef中,然後被拷貝至屏幕



此文轉自M了個j,清晰明瞭,感謝!

發佈了48 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章