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-創建新的層
一、添加一個簡單的圖層
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,清晰明瞭,感謝!