iOS - CALayer 绘图层
1、CALayer 繪圖層
在 iOS 系統中,你能看得見摸得著的東西基本上都是 UIView,比如一個按鈕、一個文本標簽、一個文本輸入框、一個圖標等等,這些都是 UIView。其實 UIView 之所以能顯示在屏幕上,完全是因為它內部的一個層。在創建 UIView 對象時,UIView 內部會自動創建一個層(即 CALayer 對象),通過 UIView 的 layer 屬性可以訪問這個層。當 UIView 需要顯示到屏幕上時,會調用 drawRect: 方法進行繪圖,并且會將所有內容繪制在自己的層上,繪圖完畢后,系統會將層拷貝到屏幕上,于是就完成了 UIView 的顯示。換句話說,UIView 本身不具備顯示的功能,是它內部的層才有顯示功能。
CALayer 的簡單使用
- CALayer 是被定義在 QuartzCore 框架中的,通過操作 CALayer 對象,可以很方便地調整 UIView 的一些界面屬性,比如:陰影、圓角大小、邊框寬度和顏色等。
2、基本繪圖層屬性設置
1、設置陰影
self.redView.layer.shadowOpacity = 1; // 陰影不透明度self.redView.layer.shadowOffset = CGSizeMake(10, 10); // 陰影偏移量self.redView.layer.shadowColor = [UIColor yellowColor].CGColor; // 陰影顏色self.redView.layer.shadowRadius = 10; // 陰影圓角半徑2、圓角半徑
self.redView.layer.cornerRadius = 75; // 主層半徑3、邊框
self.redView.layer.borderWidth = 3; // 邊框寬度self.redView.layer.borderColor = [UIColor blueColor].CGColor; // 邊框顏色4、imageView 圓角半徑設置
self.imageView.layer.cornerRadius = 75; // 主層半徑// 超出主層邊框的內容全部裁剪掉,image 在視圖層上self.imageView.layer.masksToBounds = YES; // 是否對非主層裁剪效果
3、形變屬性設置
3.1 視圖形變
1、單一形變
// 旋轉/*(CGFloat angle) 旋轉 45 度,需要輸入的參數為弧度,45/180 * M_PI,1 度 = PI/180 弧度*/[UIView animateWithDuration:1 animations:^{self.redView.transform = CGAffineTransformMakeRotation(0.25 * M_PI);}];// 縮放/*(CGFloat sx, CGFloat sy) (1, 2) 寬度和高度的放大倍數*/[UIView animateWithDuration:1 animations:^{self.redView.transform = CGAffineTransformMakeScale(1, 2);}];// 平移/*(CGFloat tx, CGFloat ty) (100, 100) 水平和垂直方向的移動距離*/[UIView animateWithDuration:1 animations:^{self.redView.transform = CGAffineTransformMakeTranslation(100, 100);}];效果
2、 疊加形變
// 旋轉 + 縮放[UIView animateWithDuration:1 animations:^{CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);self.redView.transform = CGAffineTransformScale(rotationTransform, 1.5, 1.5);}];// 旋轉 + 平移[UIView animateWithDuration:1 animations:^{CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);self.redView.transform = CGAffineTransformTranslate(rotationTransform, 100, 0);}];// 縮放 + 平移[UIView animateWithDuration:1 animations:^{CGAffineTransform scaleTransform = CGAffineTransformMakeScale(1, 1.5);self.redView.transform = CGAffineTransformTranslate(scaleTransform, 100, 0);}];// 旋轉 + 縮放 + 平移[UIView animateWithDuration:1 animations:^{CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(0.25 * M_PI);CGAffineTransform rotationScaleTransform = CGAffineTransformScale(rotationTransform, 1.5, 1.5);self.redView.transform = CGAffineTransformTranslate(rotationScaleTransform, 100, 0);}];效果
3、累加形變
// 連續旋轉[UIView animateWithDuration:1 animations:^{self.redView.transform = CGAffineTransformRotate(self.redView.transform, 0.25 * M_PI);}];// 連續縮放[UIView animateWithDuration:1 animations:^{self.redView.transform = CGAffineTransformScale(self.redView.transform, 1.5, 1.5);}];// 連續平移[UIView animateWithDuration:1 animations:^{self.redView.transform = CGAffineTransformTranslate(self.redView.transform, 50, 50);}];效果
4、還原形變
// 還原所有形變self.redView.transform = CGAffineTransformIdentity;
3.2 繪圖層形變
1、單一形變
// 旋轉/*(CGFloat angle) 旋轉 45 度,需要輸入的參數為弧度,45/180 * M_PI,1 度 = PI/180 弧度*/[UIView animateWithDuration:1 animations:^{self.redView.layer.affineTransform = CGAffineTransformMakeRotation(0.25 * M_PI);}];// 縮放/*(CGFloat sx, CGFloat sy) (1, 2) 寬度和高度的放大倍數*/[UIView animateWithDuration:1 animations:^{self.redView.layer.affineTransform = CGAffineTransformMakeScale(1, 2);}];// 平移/*(CGFloat tx, CGFloat ty) (100, 100) 水平和垂直方向的移動距離*/[UIView animateWithDuration:1 animations:^{self.redView.layer.affineTransform = CGAffineTransformMakeTranslation(100, 100);}];// 還原所有形變self.redView.layer.affineTransform = CGAffineTransformIdentity;效果
2、快速進行繪圖層形變,KVC
// 旋轉[UIView animateWithDuration:1 animations:^{[self.redView.layer setValue:@(0.25 * M_PI) forKeyPath:@"transform.rotation"];}];// 縮放[UIView animateWithDuration:1 animations:^{[self.redView.layer setValue:@1.5 forKeyPath:@"transform.scale"];}];效果
3、繪圖層 3D 形變
// 旋轉/*(CGFloat angle, CGFloat x, CGFloat y, CGFloat z) 旋轉角度,x y z 軸的坐標,為 0 時在此軸上不旋轉*/[UIView animateWithDuration:1 animations:^{self.imageView.layer.transform = CATransform3DMakeRotation(M_PI, 0, 1, 0);}];// 縮放/*(CGFloat sx, CGFloat sy, CGFloat sz),x y z 軸的縮放倍數*/[UIView animateWithDuration:1 animations:^{self.imageView.layer.transform = CATransform3DMakeScale(0.5, 0.5, 1);}];// 平移/*(CGFloat tx, CGFloat ty, CGFloat tz),x y z 軸的平移量*/[UIView animateWithDuration:1 animations:^{self.imageView.layer.transform = CATransform3DMakeTranslation(100, 100, 0);}];效果
3.3 獲取形變值
獲取旋轉的角度
// 根據 transform 獲取旋轉角度CGFloat angle = atan2(self.redView.transform.b, self.redView.transform.a);
4、創建新的繪圖層
UIView 內部默認有個 CALayer 對象(層),通過 layer 屬性可以訪問這個層。要注意的是,這個默認的層不允許重新創建,但可以往層里面添加子層。UIView 可以通過 addSubview: 方法添加子視圖,類似地,CALayer 可以通過 addSublayer: 方法添加子層。
1、添加一個簡單的圖層
// 創建圖層CALayer *myLayer = [CALayer layer];myLayer.frame = CGRectMake(100, 100, 200, 200);myLayer.backgroundColor = [UIColor redColor].CGColor;[self.view.layer addSublayer:myLayer];效果
2、添加一個顯示圖片的圖層
// 創建圖層CALayer *myLayer = [CALayer layer];myLayer.frame = CGRectMake(100, 100, 200, 200);myLayer.backgroundColor = [UIColor redColor].CGColor;// 設置圖層內容myLayer.contents = (id)[UIImage imageNamed:@"demo2.jpg"].CGImage;[self.view.layer addSublayer:myLayer];效果
3、為什么 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。
4、UIView 和 CALayer 的選擇
- 細心的朋友不難發現,其實前面的 2 個效果不僅可以通過添加層來實現,還可以通過添加 UIView 來實現。比如,第 1 個紅色的層可以用一個 UIView 來實現,第 2 個顯示圖片的層可以用一個 UIImageView 來實現。既然 CALayer 和 UIView 都能實現相同的顯示效果,那究竟該選擇誰好呢?
- 其實,對比 CALayer,UIView 多了一個事件處理的功能。也就是說,CALayer 不能處理用戶的觸摸事件,而 UIView 可以。
- 所以,如果顯示出來的東西需要跟用戶進行交互的話,用 UIView;如果不需要跟用戶進行交互,用 UIView 或者 CALayer 都可以。
- 當然,CALayer 的性能會高一些,因為它少了事件處理的功能,更加輕量級。
5、UIView 和 CALayer 的關系
- UIView 可以通過 subviews 屬性訪問所有的子視圖,類似地,CALayer 也可以通過 sublayers 屬性訪問所有的子層。
- UIView 可以通過 superview 屬性訪問父視圖,類似地,CALayer 也可以通過 superlayer 屬性訪問父層。
下面再看一張 UIView 和 CALayer 的關系圖,如果兩個 UIView 是父子關系,那么它們內部的 CALayer 也是父子關系。
5、繪圖層隱式動畫屬性
在前面已經提到,每一個 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)1、隱式動畫屬性設置
self.myLayer.bounds = CGRectMake(0, 0, 100, 100);self.myLayer.backgroundColor = [UIColor greenColor].CGColor;self.myLayer.position = CGPointMake(arc4random_uniform(200) + 20, arc4random_uniform(400) + 50);self.myLayer.transform = CATransform3DMakeRotation(arc4random_uniform(360), 0, 0, 1);效果
2、可以通過動畫事務(CATransaction)關閉默認的隱式動畫效果。
[CATransaction begin];[CATransaction setDisableActions:YES];self.myLayer.position = CGPointMake(10, 10);[CATransaction commit];
6、繪圖層 position 和 anchorPoint 屬性
position 和 anchorPoint 屬性都是 CGPoint 類型的。
position :位置,可以用來設置 CALayer 在父層中的位置,它是以父層的左上角為坐標原點(0, 0)。anchorPoint :錨點,稱為 "定位點",它決定著 CALayer 身上的哪個點會在 position 屬性所指的位置。它的 x、y 取值范圍都是 0~1,默認值為 (0.5, 0.5)。1、anchorPoint 為默認值(0.5, 0.5)
CALayer *myLayer = [CALayer layer];myLayer.backgroundColor = [UIColor redColor].CGColor;myLayer.bounds = CGRectMake(0, 0, 100, 100);// 設置層的位置myLayer.position = CGPointMake(100, 100);[self.view.layer addSublayer:myLayer];設置了 myLayer 的 position 為(100, 100),又因為 anchorPoint 默認是(0.5, 0.5),所以最后的效果是 myLayer 的中點會在父層的(100, 100)位置。
2、anchorPoint 為(0, 0)
若將 anchorPoint 改為(0, 0),myLayer 的左上角會在(100, 100)位置。
myLayer.anchorPoint = CGPointMake(0, 0);
3、anchorPoint 為(1, 1)
若將 anchorPoint 改為(1, 1),myLayer 的右下角會在(100, 100)位置。
myLayer.anchorPoint = CGPointMake(1, 1);
4、anchorPoint 為(0, 1)
將 anchorPoint 改為(0, 1),myLayer 的左下角會在(100, 100)位置。
myLayer.anchorPoint = CGPointMake(0, 1);
7、自定義繪圖層
7.1 自定義繪圖層方法 1
創建一個 CALayer 的子類,然后覆蓋 drawInContext: 方法,使用 Quartz2D API 進行繪圖。
QCLayer.h
@interface QCLayer : CALayer@endQCLayer.m
@implementation QCLayer#pragma mark 繪制一個實心三角形- (void)drawInContext:(CGContextRef)ctx {// 設置為藍色CGContextSetRGBFillColor(ctx, 0, 0, 1, 1);// 設置起點CGContextMoveToPoint(ctx, 50, 0);// 從 (50, 0) 連線到 (0, 100)CGContextAddLineToPoint(ctx, 0, 100);// 從 (0, 100) 連線到 (100, 100)CGContextAddLineToPoint(ctx, 100, 100);// 合并路徑,連接起點和終點CGContextClosePath(ctx);// 繪制路徑CGContextFillPath(ctx);}@endViewController.m
QCLayer *layer = [QCLayer layer];// 設置層的寬高layer.bounds = CGRectMake(0, 0, 100, 100);// 設置層的位置layer.position = CGPointMake(100, 100);// 開始繪制圖層,需要調用這個方法,才會觸發 drawInContext: 方法的調用,然后進行繪圖[layer setNeedsDisplay];[self.view.layer addSublayer:layer];效果
7.2 自定義繪圖層方法 2
- 設置 CALayer 的 delegate,然后讓 delegate 實現 drawLayer:inContext: 方法,當 CALayer 需要繪圖時,會調用 delegate 的 drawLayer:inContext: 方法進行繪圖。
這里要注意的是:不能再將某個 UIView 設置為 CALayer 的 delegate,因為 UIView 對象已經是它內部根層的 delegate,再次設置為其他層的 delegate 就會出問題。UIView 和它內部 CALayer 的默認關系圖:
創建新的層,設置 delegate,然后添加到控制器的 view 的 layer 中。
CALayer *layer = [CALayer layer];// 設置 delegate,這里的 self 是指控制器layer.delegate = self;// 設置層的寬高layer.bounds = CGRectMake(0, 0, 100, 100);// 設置層的位置layer.position = CGPointMake(100, 100);// 開始繪制圖層,需要調用這個方法,才會通知 delegate 進行繪圖[layer setNeedsDisplay];[self.view.layer addSublayer:layer];讓 CALayer 的 delegate(前面設置的是控制器)實現 drawLayer:inContext: 方法
#pragma mark 畫一個矩形框- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {// 設置藍色CGContextSetRGBStrokeColor(ctx, 0, 0, 1, 1);// 設置邊框寬度CGContextSetLineWidth(ctx, 10);// 添加一個跟層一樣大的矩形到路徑中CGContextAddRect(ctx, layer.bounds);// 繪制路徑CGContextStrokePath(ctx);}效果
7.3 UIView 的詳細顯示過程
當 UIView 需要顯示時,它內部的層會準備好一個 CGContextRef(圖形上下文),然后調用 delegate(這里就是 UIView)的 drawLayer:inContext: 方法,并且傳入已經準備好的 CGContextRef 對象。而 UIView 在 drawLayer:inContext: 方法中又會調用自己的 drawRect: 方法
平時在 drawRect: 中通過 UIGraphicsGetCurrentContext() 獲取的就是由層傳入的 CGContextRef 對象,在 drawRect: 中完成的所有繪圖都會填入層的 CGContextRef 中,然后被拷貝至屏幕。
8、漸變圖層
漸變圖層 CAGradientLayer : CALayer
添加漸變圖層
CAGradientLayer *gradientLayer = [CAGradientLayer layer];gradientLayer.frame = self.imageView.bounds;// 設置透明度gradientLayer.opacity = 0.5;// 設置漸變顏色gradientLayer.colors = @[(id)[UIColor clearColor].CGColor, (id)[UIColor blackColor].CGColor];[self.imageView.layer addSublayer:gradientLayer]; CAGradientLayer *gradientLayer = [CAGradientLayer layer];gradientLayer.frame = self.imageView.bounds;// 設置透明度gradientLayer.opacity = 0.5;// 設置漸變顏色gradientLayer.colors = @[(id)[UIColor redColor].CGColor,(id)[UIColor greenColor].CGColor,(id)[UIColor yellowColor].CGColor];// 設置漸變定位點gradientLayer.locations = @[@0.1, @0.4, @0.8];// 設置漸變開始點,取值 0~1gradientLayer.startPoint = CGPointMake(0, 1);[self.imageView.layer addSublayer:gradientLayer];效果
9、復制圖層
復制圖層 CAReplicatorLayer : CALayer,可以把圖層里面所有子層復制
添加復制圖層
CAReplicatorLayer *repLayer = [CAReplicatorLayer layer];repLayer.frame = self.view.bounds;[self.view.layer addSublayer:repLayer];// 添加子層[repLayer addSublayer:self.imageView.layer];// 設置有多少個子層,包括原始層repLayer.instanceCount = 4;// 設置子層偏移量,不包括原始層,相對于原始層 x 偏移repLayer.instanceTransform = CATransform3DMakeTranslation(70, 0, 0);// 設置子層背景色repLayer.instanceColor = [UIColor greenColor].CGColor;// 設置子層陰影repLayer.instanceAlphaOffset = -0.1;repLayer.instanceRedOffset = -0.1;repLayer.instanceGreenOffset = -0.1;repLayer.instanceBlueOffset = -0.1;// 設置子層動畫延遲時間,子層有動畫時有效repLayer.instanceDelay = 0; CAReplicatorLayer *repLayer = [CAReplicatorLayer layer];repLayer.frame = self.view.bounds;[self.view.layer addSublayer:repLayer];// 添加子層CALayer *layer = [CALayer layer];layer.anchorPoint = CGPointMake(0.5, 1);layer.position = CGPointMake(100, 300);layer.bounds = CGRectMake(0, 0, 30, 150);layer.backgroundColor = [UIColor whiteColor].CGColor;[repLayer addSublayer:layer];// 添加子層動畫CABasicAnimation *anim = [CABasicAnimation animation];anim.keyPath = @"transform.scale.y";anim.toValue = @0.1;anim.duration = 0.5;anim.repeatCount = MAXFLOAT;anim.autoreverses = YES;[layer addAnimation:anim forKey:nil];// 設置子層repLayer.instanceCount = 4;repLayer.instanceTransform = CATransform3DMakeTranslation(45, 0, 0);repLayer.instanceDelay = 0.1;repLayer.instanceColor = [UIColor greenColor].CGColor;repLayer.instanceGreenOffset = -0.3;效果
轉載于:https://www.cnblogs.com/QianChia/p/6348043.html
總結
以上是生活随笔為你收集整理的iOS - CALayer 绘图层的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 回溯算法题总结
- 下一篇: 两个数字交换(不使用临时变量)