##一、CALayer概述git
CALayer
類在概念上和UIView
相似,一樣也是一些被層級關係 樹管理的矩形塊,一樣也能夠包含一些內容(像圖片,文本或者背景色),管理子圖層的位置。它們有一些⽅法和屬性用來作動畫和變換。 CALayer是CoreAnimation部分的內容,CALayer的概念相似於photoshop中層的概念,每一個UIView都有一個根CALayer,每一個CALayer又能夠添加子CALayer,從結構上來看CALayer是一種樹形結構,UIView的繪製工做都交由CALayer完成。github
和UIView最大的不一樣是CALayer
不處理⽤用戶的交互。由於CALayer
並不清楚具體的響應鏈(iOS經過視圖層級關係用來傳送觸摸事件的機制),因而它並不可以響應事件,即便它提供了一些方法來判斷是否一個觸點在圖層的範圍以內。數據結構
##二、平行的層級關係 每個UIview
都有一個CALayer
實例的圖層屬性,也就是所謂的backing layer,視圖的職責就是建立並管理這個圖層,以確保當子視圖在層級關係中添加或者被移除的時候,他們關聯的圖層也一樣對應在層級關係樹當中有相同的操做。 實際上,這些背後關聯的圖層纔是真正用來在屏幕上顯示和作畫,UIView
僅僅是對CALayer
的一個封裝,而後提供了處理觸摸的具體功能,以及CoreAnimation
高級接口。可是爲何iOS要基於UIView和CALayer提供兩個平行的層級關係呢?爲何不⽤一個簡單的層級來處理全部事情呢?緣由在於要作職責分離,這樣也能避免不少重複代碼。在iOS和Mac OS兩個平臺上,事件和⽤戶交互有不少地方的不一樣,基於多點觸控的用戶界⾯和基於⿏標鍵盤有着本質的區別,這就是爲何iOS有UIKit和UIView,可是Mac OS有AppKit和NSView的緣由。他們功能上很類似,可是在實現上有着顯著的區別。ide
實際上,這裏並非兩個層級關係,而是四個,每個都扮演不一樣的角色,除了視圖層級和圖層樹以外,還存在呈現樹和渲染樹.函數
呈現樹:呈現樹包含了當前動畫發生時候將要顯示的值,例如你要給圖層背景顏色設置新的值的時候,它會當即修改圖層樹裏面相應的值。可是在呈現樹裏面背景顏色值在將要顯示給用戶的時候才被更新爲新值。oop
渲染樹:渲染樹是私有的,你沒法訪問到,渲染樹在渲染圖層的時候使用呈現樹的值,爲了避免阻塞主線程,渲染的過程是在單獨的進程或線程中進行的,因此你會發現Animation的動畫並不會阻塞主線程。學習
前⾯面已經講過,UIView是對CALayer的封裝,那爲何咱們還要學習CALayer呢?由於UIView封裝的 API在有些狀況下並不能知足咱們的需求,好比:字體
1.陰影,圓⾓角,邊框 2.3D變換 3.⾮矩形範圍 4.遮罩 5.⾮線性動畫
##三、CALayer經常使用屬性 咱們能夠進入CALayer.h中查看CALayer支持的屬性,其中註解中標註Animation的屬性表示支持隱式動畫,當這些屬性的值改變時系統自帶了平滑過渡的動畫效果(非根Layer支持)動畫
下表列出了CALayer經常使用的屬性:線程
屬性 | 說明 | 是否支持隱式動畫 |
---|---|---|
anchorPoint | 和中心點position重合的一個點,稱爲「錨點」,錨點的描述是相對於x、y位置比例而言的默認在圖像中心點(0.5,0.5)的位置 | 是 |
backgroundColor | 圖層背景顏色 | 是 |
borderColor | 邊框顏色 | 是 |
borderWidth | 邊框寬度 | 是 |
bounds | 圖層大小 | 是 |
contents | 圖層顯示內容,例如能夠將圖片做爲圖層內容顯示 | 是 |
contentsRect | 圖層顯示內容,例如能夠將圖片做爲圖層內容顯示 | 是 |
contentsRect | 圖層顯示內容的大小和位置 | 是 |
cornerRadius | 圓角半徑 | 是 |
doubleSided | 圖層背面是否顯示,默認爲YES | 否 |
frame | 圖層大小和位置,不支持隱式動畫,因此CALayer中不多使用frame,一般使用bounds和position代替 | 否 |
zPosition | 圖層的重疊順序 | 是 |
hidden | 是否隱藏 | 是 |
mask | 圖層蒙版 | 是 |
maskToBounds | 是否剪切超出父圖層邊界的部分,默認爲NO | 是 |
opacity | 透明度 ,相似於UIView的alpha | 是 |
position | 圖層中心點位置,相似於UIView的center | 是 |
shadowColor | 陰影顏色 | 是 |
shadowOffset | 陰影偏移量 | 是 |
shadowOpacity | 陰影透明度,注意默認爲0,若是設置陰影必須設置此屬性 | 是 |
shadowPath | 陰影的形狀 | 是 |
shadowRadius | 陰影模糊半徑 | 是 |
sublayers | 設置多個子圖層 | 是 |
sublayerTransform | 子圖層形變 | 是 |
transform | 圖層形變 | 是 |
建立layer並添加到根layer上:
CALayer *layer = [[CALayer alloc] init]; layer.frame = CGRectMake(10, 320, 330, 200); layer.backgroundColor = [UIColor blueColor].CGColor; // 將圖層添加到父圖層 [self.view.layer addSublayer:layer];
填充圖片內容,須要將 UIImage 橋接(__bridge)到CGImage:
layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"image4.jpg"].CGImage);
使用layer CATextLayer 子類填充文字:
CATextLayer *textLayer = [[CATextLayer alloc] init]; textLayer.frame = CGRectMake(10, 550, 300, 30); textLayer.string = @"這是layer填充的文字內容"; //字體顏色 textLayer.foregroundColor = [UIColor blackColor].CGColor; textLayer.backgroundColor = [UIColor redColor].CGColor; textLayer.font = (__bridge CFTypeRef _Nullable)([UIFont systemFontOfSize:20 weight:500]); textLayer.fontSize = 20; textLayer.alignmentMode = @"center"; // textLayer.truncationMode = @"middle"; [self.view.layer addSublayer:textLayer];
##四、CALayer圖層繪製
CALayer *layer = [CALayer layer]; // layer.frame = CGRectMake(0, 100, 350, 200); layer.backgroundColor = [UIColor orangeColor].CGColor; // 一、bounds: 尺寸 layer.bounds = CGRectMake(0, 0, 220, 220); // 二、position: 定位點 layer.position = self.view.center; // 三、錨點、支點:決定layer上的哪一個點在 position 點上,默認(0.5, 0.5),範圍:(0,0) ~ (1,1) layer.anchorPoint = CGPointMake(0.5, 0.5); // 四、z方向的層級 layer.zPosition = 2; // 五、設置圓角:爲直徑的一半時會成圓形 layer.cornerRadius = 110; // 六、填充內容 layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"image1.jpg"].CGImage); //設置背景顏色 layer.backgroundColor = [UIColor orangeColor].CGColor; // 七、是否能夠裁剪多餘的圖層 layer.masksToBounds = YES; // 八、設置邊框寬度和顏色 //layer.borderWidth = 5; //layer.borderColor = [UIColor lightGrayColor].CGColor; [self.view.layer addSublayer:layer]; }
實現效果:
須要注意的是上面代碼中繪製圖片圓形裁切效果時若是不設置masksToBounds是沒法顯示圓形,可是對於其餘圖形卻沒有這個限制。緣由就是當繪製一張圖片到圖層上的時候會從新建立一個圖層添加到當前圖層,這樣一來若是設置了圓角以後雖然底圖層有圓角效果,可是子圖層仍是矩形,只有設置了masksToBounds爲YES讓子圖層按底圖層剪切才能顯示圓角效果。
若是設置了masksToBounds=YES
以後確實能夠顯示圖片圓角效果,但遺憾的是設置了這個屬性以後就沒法設置陰影效果。由於masksToBounds=YES
就意味着外邊框不能顯示,而陰影偏偏做爲外邊框繪製的,這樣兩個設置就產生了矛盾。要解決這個問題不妨換個思路:使用兩個大小同樣的圖層,下面的圖層負責繪製陰影,上面的圖層用來顯示圖片。
//陰影圖層 CALayer *layerShadow = [[CALayer alloc]init]; layerShadow.bounds = CGRectMake(0, 0, 220, 220); layerShadow.position= self.view.center; layerShadow.cornerRadius = 110; //陰影顏色 layerShadow.shadowColor = [UIColor grayColor].CGColor; //陰影偏移量 layerShadow.shadowOffset = CGSizeMake(5, 1); //陰影透明度 layerShadow.shadowOpacity = 1; layerShadow.borderColor = [UIColor whiteColor].CGColor; layerShadow.borderWidth = 5; [self.view.layer addSublayer:layerShadow]; }
實現效果:
##五、CALayer3D變換
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { UITouch *touch=[touches anyObject]; CGFloat width=layer.bounds.size.width; if (width==Width) { width=Width*2; // 組合 CATransform3D CATransform3D transform_01 = CATransform3DScale(layer.transform, 1, 1, 1); CATransform3D transform_02 = CATransform3DRotate(layer.transform, M_PI/6, 0, 0, 1); layer.transform = CATransform3DConcat(transform_01, transform_02); layer.cornerRadius = width/2; layer.backgroundColor = [UIColor blueColor].CGColor; }else{ width=Width; layer.cornerRadius = 0; layer.backgroundColor = [UIColor orangeColor].CGColor; } layer.bounds=CGRectMake(0, 0, width, width); layer.position=[touch locationInView:self.view]; } @end
實現效果:
1:CATransform3DMakeRotation(CGFloat angle, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>)
若是x=1,y=0,z=0則繞x軸旋轉angle角度
若是x=0,y=1,z=0則繞y軸旋轉angle角度
若是x=0,y=0,z=1則繞z軸旋轉angle角度
若是x=1,y=1,z=0則繞x軸和y軸夾角旋轉angle角度
若是x=1,y=1,z=1則繞3軸夾角旋轉angle角度
上訴的旋轉中心都是layer的錨點(anchorPoint)
2:CATransform3DRotate(<#CATransform3D t#>, <#CGFloat angle#>, <#CGFloat x#>, <#CGFloat y#>, <#CGFloat z#>)
功能與上一函數相似,但能夠疊加一個CATransform3D效果
3:CATransform3DMakeScale(<#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)
用於縮放,三個參數是x軸,Y軸,z軸上的縮放程度,縮放中心是layer的錨點
4:CATransform3DScale(<#CATransform3D t#>, <#CGFloat sx#>, <#CGFloat sy#>, <#CGFloat sz#>)
功能與上一函數相似,但能夠疊加一個CATransform3D效果
5:CATransform3DMakeTranslation(CGFloat tx, <#CGFloat ty#>, <#CGFloat tz#>)
用於平移
6:CATransform3DTranslate(<#CATransform3D t#>, <#CGFloat tx#>, <#CGFloat ty#>, <#CGFloat tz#>)
用於移動
7:CATransform3DConcat(CATransform3D a, <#CATransform3D b#>)
將兩個CATransform3D效果疊加起來
咱們也能夠經過KVC更方便的設置transform的屬性
[layer setValue:@M_PI forKeyPath:@"transform.rotation.x"];
能夠經過KVC設置如下與transform相關的屬性
transform.rotation.x
``transform.rotation.y`
transform.rotation.z
transform.scale.x
transform.scale.y
transform.scale.z
transform.translation.x
transform.translation.y
transform.translation.z
##六、CALayer事務 CALayer中"Animatable"屬性變化都在CATrasaction的管理內,以前提到的屬性支持隱式動畫是指在某次Runroop中修改"Animatable"時,若是沒有設置事務,則會自動建立一個CATransaction,並在當前線程的下一個RunLoop中commit這個CATransaction。
事務能夠被嵌套,容許你禁用部分動畫的行爲或者在屬性被修改的時候產生 的動畫使用不一樣的時間。僅當最外層的事務被提交的時候,動畫纔會發生。
事務開啓:[CATransaction begin]
禁止動畫效果:[CATransaction setDisableActions:YES];
事務提交:[CATransaction commit]
咱們能夠經過事務控制動畫的時長,甚至禁止動畫效果,還能夠設置completionBlock,噹噹前CATransaction的全部動畫執行結束後,,completionBlock會被調用
##Demo下載地址: https://github.com/fuxinto/HfxDemo