CoreAnimation
本次教程分為兩個部分,分別為理論和實戰(zhàn),實戰(zhàn)部分,主要就是一些代碼,對理論部分的解析,下面讓我們進入下面我們的第一部分CoreAnimation With Swift 理論
CALayer基礎(chǔ)
UIView之所以能顯示在屏幕上,完全是因為它內(nèi)部的一個圖層.當UIView需要顯示到屏幕上時,會調(diào)用drawRect:方法進行繪圖,并且會將所有內(nèi)容繪制在自己的圖層上,繪圖完畢后,系統(tǒng)會將圖層拷貝到屏幕上,于是就完成了UIView的顯示。CALayer是用來管理位圖的state,以及如何被渲染出來。
CALayer基本屬性
CALayer中anchorpoint是圖層的關(guān)鍵,anchorpoint的位置發(fā)生變化,包括CALayer的position等屬性相應(yīng)的就會發(fā)生變化。anchorpoint是用單位坐標的(x,y軸都是從0到1,默認的anchorpoint的坐標為(0.5,0.5))
position:CGPoint用來設(shè)置CALayer在父層中的位置
anchorPoint:CGPoint決定著CALayer身上的哪個點會在position屬性所指的位置
contentsGravity
當圖片大小不是適合當前view的時候,你可能會使用view.contentMode來設(shè)置縮放和位置,其實他是通過控制CALayer的contentGravity屬性來實現(xiàn)的
contentsScale
由于layer不能區(qū)分分辨率,contentScale用來定義像素尺寸到layer尺寸的拉伸比率,默認值是1
contentsRect
contentsRect使用的是單位坐標,表示的是需要顯示的內(nèi)容的尺寸
contentsCenter
contentsCenter:CGRect定義了layer內(nèi)部一個可以被拉伸的區(qū)域,外部則是一個固定的邊界。這個屬性將一個layer分為9個部分。根據(jù)contentsGravity的設(shè)定,拉伸相應(yīng)地區(qū)域??梢詤⒖嘉⑿帕奶煊涗浬夏莻€氣泡
layer的視覺效果
圓角
CALayer有一個cornerRadius屬性用來制作layer的圓角,默認情況下該屬性只作用于backgroundColor,而對于sublayer和背景圖都沒有效果,但是如果配合masksToBounds=YES的設(shè)置,可以對layer內(nèi)所有元素生效。
邊框
CALayer通過borderWidth和borderColor兩個屬性的組合來定義border的寬度和顏色
陰影
通過設(shè)置shadowOpacity屬性為一個大于0的值,可以給任意layer添加一個背景陰影。取值范圍是0.0到1.0,表示全透明到非透明。同時還可以通過shadowColor(默認為黑)、shadowOffset(距離和方向)和shadowRadius(模糊度)三個屬性來修改陰影的外觀。不同于border圍繞在layer的bounds,shadow是圍繞在真正的內(nèi)容外的??梢远xshadowPath來自定義陰影路徑
遮罩(Masking)
mask屬性接受另外一個CALayer值,可以被用作將當前l(fā)ayer裁切成給定layer的輪廓。masksToBounds則是讓子視圖不超過父視圖的范圍
Opacity
這里要說得是當子圖層和父圖層都設(shè)置了Opacity的時候(比如父類為0.5,子類為0.5 ,那么實際上子類的透明度為0.5*0.5+0.5(父類)=0.75).而下面是你設(shè)置透明度和alpha等需要注意的
1.opaque 直接不再繪制這層layer下面的layer(效率高)
2.alpha會影響在layer上視圖的透明度
- 在app的Info.plist中添加UIViewGroupOpacity=YES。這會作用于當前app下的所有范圍,可能還有點小的性能浪費。
- 通過
CALayer的shouldRasterize=YES,將layer及其子layer全部打散成一個平面的圖形,再使Opacity生效就可以達到我們想要的效果了。由于rasterizationScale默認等于1.0,會使得高清屏幕下圖片像素化,所以我們還要同時設(shè)置一下:layer.rasterizationScale = UIScreen.mainScreen().scale
Transforms
UIView的transform屬性類型是GAffineTransform,它用于對view進行二維空間下的旋轉(zhuǎn)、拉伸或變形。其實他就是對CALayer的封裝。CALayer同時也有一個transform屬性,它的類型是CATransform3D(CATransform3D是一個4x4的矩陣,從而支持了對于垂直屏幕的z軸的變換效果。其中m34屬性通常被用來計算x和y值的縮放比例,默認值是0)。CALayer實現(xiàn)仿射變化的屬性是affineTransform(UIView就是對這個的封裝)。CATransform3D的定義和它各個參數(shù)的作用如下。
struct CATransform3D {
m11(x縮放), m12(y切變),m13(旋轉(zhuǎn)), m14();
m21(x切變), m22(y縮放), m23(), m24();
m31(旋轉(zhuǎn)), m32( ), m33(),
m34(透視效果,要操作的這個對象要有旋轉(zhuǎn)的角度,否則沒有效果。正直/負值都有意義)
m41(x平移), m42(y平移), m43(z平移), m44();
};
CATransform3D相關(guān)的函數(shù)
CATransform3D CATransform3DTranslate (CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz);
t:就可以理解為:函數(shù)的疊加,效果的疊加。
CATransform3D CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz);
sx:X軸縮放,代表一個縮放比例,一般都是 0 --- 1 之間的數(shù)字。sy:Y軸縮放。
sz:整體比例變換時,也就是m11(sx)== m22(sy)時,若m33(sz)>1,圖形整體縮小,若0<1,圖形整體放大,若m33(sz)<0,發(fā)生關(guān)于原點的對稱等比變換。
CATransform3D CATransform3DMakeRotation (CGFloat angle, CGFloat x, CGFloat y, CGFloat z);
旋轉(zhuǎn)效果。angle:旋轉(zhuǎn)的弧度,所以要把角度轉(zhuǎn)換成弧度:角度 * M_PI / 180。
x:向X軸方向旋轉(zhuǎn)。值范圍-1 --- 1之間y:向Y軸方向旋轉(zhuǎn)。值范圍-1 --- 1之間
z:向Z軸方向旋轉(zhuǎn)。值范圍-1 --- 1之間
CALayer的子類
CAGradientLayer
實現(xiàn)CALayer的漸變色
// 漸變圖層
let gradientL = CAGradientLayer()
gradientL.frame = bottomView.bounds;
gradientL.opacity = 0;
gradientL.colors = @[UIColor.clearColor().CGColor,UIColor blackColor().CGColor];
CAReplicatorLayer
CAReplicatorLayer復(fù)制圖層,把子圖層放在CAReplicatorLayer中,,可以把圖層里面所有子層復(fù)制.這個圖層用于,有許多相同的子視圖做相同的動畫效果的時候,這個可以復(fù)制這些子視圖,并且為這些子視圖加上延遲等效果
let repL = CAReplicatorLayer()
repL.frame = _lightView.bounds;
_lightView.layer.addSublayer(repL)
// 子圖層
let layer = CALayer()
layer.anchorPoint = CGPointMake(0.5, 1)
layer.position = CGPointMake(15, _lightView.bounds.size.height)
layer.bounds = CGRectMake(0, 0, 30, 150)
repL.addSublayer(layer)
let anim = CABasicAnimation()
anim.keyPath = "transform.scale.y"
anim.toValue = 0.1
anim.duration = 0.5
anim.repeatCount = MAXFLOAT;
// 設(shè)置動畫反轉(zhuǎn)
anim.autoreverses = YES;
layer.addAnimation(anim,forKey:nil)
// 復(fù)制層中子層總數(shù)
// instanceCount:表示復(fù)制層里面有多少個子層,包括原始層
repL.instanceCount = 3;
// 設(shè)置復(fù)制子層偏移量,不包括原始層,相對于原始層x偏移,instanceTransform決定如何排序
repL.instanceTransform = CATransform3DMakeTranslation(45, 0, 0);
// 設(shè)置復(fù)制層動畫延遲時間
repL.instanceDelay = 0.1;
// 如果設(shè)置了原始層背景色,就不需要設(shè)置這個屬性
repL.instanceColor = UIColor.greenColor().CGColor
repL.instanceGreenOffset = -0.3;
UIView和CALayer的選擇
其實,對比CALayer,UIView多了一個事件處理的功能。也就是說,CALayer不能處理用戶的觸摸事件,而UIView可以.所以,如果顯示出來的東西需要跟用戶進行交互的話,用UIView;如果不需要跟用戶進行交互,用UIView或者CALayer都可以當然,CALayer的性能會高一些,因為它少了事件處理的功能,更加輕量級。雖然CALayer不感知responder chain,但是他通過提供-containPoint:和-hitTest:兩個方法來幫助你定位事件所在layer。
我們?yōu)槭裁匆私?code>CALayer的存在,因為他有一些UIView不能做的事情:
- 陰影、圓角、多彩的border
- 3D變化和定位
- 非矩形的邊界
- alpha遮罩
- 多步非線性動畫
layer的繪制
在UIView的子類中可以通過實現(xiàn)-drawRect:方法來自定義繪圖,CALayer的自定義繪圖則通過layer的delegate對象(CALayerDelegate 協(xié)議)的以下兩個方法來實現(xiàn):
// 方法1 需要手動調(diào)用layer.display()方法來調(diào)用
func displayLayer(_ layer: CALayer!);
// 方法2. 如果方法1沒有實現(xiàn),則調(diào)方法2。需要手動調(diào)用layer.display()方法來調(diào)用
func drawLayer(_ layer: CALayer!, inContext ctx,: CGContext )
layer tree
CoreAnimation是一個合成引擎,它的工作是盡可能快的將不同的可視化的內(nèi)容合成到屏幕上。這個可視化內(nèi)容是不同的層(layer),構(gòu)成一個叫做層樹(layer tree)的層次結(jié)構(gòu)。層樹主要由三個部分組成:

- 模型層樹:app交互的地方,存儲目標值的地方
- 外觀層樹:展示值得地方(從origin到target),在這一層獲取動畫過程中的當前值,這一層通過
presentationLayer()方法獲取。這層就是所看到的動畫,但是實際上控件上的視圖的frame之類的屬性沒有發(fā)生變化。 - 渲染層樹:私有的方法
隱式動畫
每一個UIView內(nèi)部都默認關(guān)聯(lián)著一個CALayer,我們可用稱這個Layer為Root Layer(根層)。所有的非Root Layer(也就是手動創(chuàng)建的CALayer對象),都存在著隱式動畫,也就是說當對非Root Layer的部分屬性進行修改時,默認會自動產(chǎn)生一些動畫效果而這些屬性稱為Animatable Properties(可動畫屬性)常見的Animatable Properties:
-
bounds:用于設(shè)置CALayer的寬度和高度。修改這個屬性會產(chǎn)生縮放動畫 -
backgroundColor:用于設(shè)置CALayer的背景色。修改這個屬性會產(chǎn)生背景色的漸變動畫 -
position:用于設(shè)置CALayer的位置。修改這個屬性會產(chǎn)生平移動畫
當然這個隱式的動畫可以通過動畫事務(wù)(CATransaction)關(guān)閉,它負責(zé)成批的把多個圖層樹的修改作為一個原子更新到渲染樹。
核心動畫
核心動畫繼承結(jié)構(gòu)
核心動畫的繼承關(guān)系如下

從上圖中可以看到核心動畫中所有類都遵守
CAMediaTiming。CAAnaimation是所有動畫類的父類,是個抽象類,不具備動畫效果.CAAnimationGroup和CATransition才有動畫效果,CAAnimationGroup是個動畫組,可以同時進行縮放,旋轉(zhuǎn)。CATransition是轉(zhuǎn)場動畫,界面之間跳轉(zhuǎn)都可以用轉(zhuǎn)場動畫。CAPropertyAnimation也是個抽象類,本身不具備動畫效果,只有子類才有.CABasicAnimation基本動畫,做一些簡單效果CAKeyframeAnimation幀動畫,做一些連續(xù)的流暢的動畫.給一個圖層添加動畫的步驟為如下
- 創(chuàng)建
CALayer - 初始化一個
CAAnimation對象,并設(shè)置一些動畫相關(guān)屬性 - 通過調(diào)用
CALayer的addAnimation:forKey:方法,增加CAAnimation對象到CALayer中,這樣就能開始執(zhí)行動畫了 - 通過調(diào)用
CALayer的removeAnimationForKey:方法可以停止CALayer中的動畫
CAAnimation的屬性介紹
CAAnimation是所有動畫對象的父類,負責(zé)控制動畫的持續(xù)時間和速度,是個抽象類,不能直接使用.下面需要對它的一些屬性的介紹
CAMediaTiming協(xié)議的屬性
- duration:動畫的持續(xù)時間
- repeatCount:重復(fù)次數(shù),無限循環(huán)可以設(shè)置HUGE_VALF或者MAXFLOAT
- repeatDuration:重復(fù)時間
- fillMode:決定當前對象在非active時間段的行為。比如動畫開始之前或者動畫結(jié)束之后. 因為動畫是在presenting tree上展現(xiàn)的,動畫結(jié)束后是展現(xiàn)的模型樹,presenting tree將被移除。
- beginTime:可以用來設(shè)置動畫延遲執(zhí)行時間,若想延遲2s,就設(shè)置為CACurrentMediaTime()+2,CACurrentMediaTime()為圖層的當前時間
CAAnimation的屬性
- removedOnCompletion:默認為YES,代表動畫執(zhí)行完畢后就從圖層上移除,圖形會恢復(fù)到動畫執(zhí)行前的狀態(tài)。如果想讓圖層保持顯示動畫執(zhí)行后的狀態(tài),那就設(shè)置為NO,不過還要設(shè)置fillMode為kCAFillModeForwards
- timingFunction:速度控制函數(shù),控制動畫運行的節(jié)奏如:kCAMediaTimingFunctionLinear等
- delegate:動畫代理(
CAAnimationDelegate)代理方法監(jiān)聽動畫的開始結(jié)束等狀態(tài)
CABasicAnimation
隨著動畫的進行,在長度為duration的持續(xù)時間內(nèi),keyPath相應(yīng)屬性的值從fromValue漸漸地變?yōu)?code>toValue.keyPath內(nèi)容是CALayer的可動畫Animatable屬性如果fillMode=kCAFillModeForwards同removedOnComletion=false,那么在動畫執(zhí)行完畢后,圖層會保持顯示動畫執(zhí)行后的狀態(tài)。但在實質(zhì)上,圖層的屬性值還是動畫執(zhí)行前的初始值,并沒有真正被改變。(只是沒有移除掉presenting tree)
**注意:核心動畫一切都是假象,并不會真實的改變圖層的屬性值,如果以后做動畫的時候,不需要與用戶交互,通常用核心動畫(轉(zhuǎn)場)。UIView動畫必須通過修改屬性的真實值,才有動畫效果。
**
CAKeyframeAnimation
CAKeyframeAnimation會使用一個NSArray保存動畫的屬性數(shù)值
- values:上述的NSArray對象。里面的元素稱為“關(guān)鍵幀”(keyframe)。動畫對象會在指定的時間(duration)內(nèi),依次顯示values數(shù)組中的每一個關(guān)鍵幀
- path:可以設(shè)置一個CGPathRef、CGMutablePathRef,讓圖層按照路徑軌跡移動。path只對
CALayer的anchorPoint和position起作用。如果設(shè)置了path,那么values將被忽略 - keyTimes:可以為對應(yīng)的關(guān)鍵幀指定對應(yīng)的時間點,其取值范圍為0到1.0,keyTimes中的每一個時間值都對應(yīng)values中的每一幀。如果沒有設(shè)置keyTimes,各個關(guān)鍵幀的時間是平分的
CAAnimationGroup
CAAnimationGroup可以保存一組動畫對象,將CAAnimationGroup對象加入層后,組中所有動畫對象可以同時并發(fā)運行,主要的屬性有
- animations:用來保存一組動畫對象的Array默認情況下,一組動畫對象是同時運行的,也可以通過設(shè)置動畫對象的beginTime屬性來更改動畫的開始時間
CATransition
CATransition用于做轉(zhuǎn)場動畫,能夠為層提供移出屏幕和移入屏幕的動畫效果。UINavigationController就是通過CATransition實現(xiàn)了將控制器的視圖推入屏幕的動畫效果.主要的動畫屬性有:
- type:動畫過渡類型
- subtype:動畫過渡方向
- startProgress:動畫起點(在整體動畫的百分比)
- endProgress:動畫終點(在整體動畫的百分比)
type屬性控制著轉(zhuǎn)場動畫過渡效果

可以通過
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion;
實現(xiàn)單視圖的轉(zhuǎn)場,而兩個視圖則得通過下面的方法
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion
定時器
當你需要定時的執(zhí)行同一個效果的動畫的時候,就需要使用定時器,iOS中有兩種定時器,NSTimer和CADisplayLink。NSTimer就是普通意義上的定時器,下面主要是講CADisplayLink
CADisplayLink
CADisplayLink是一種以屏幕刷新頻率觸發(fā)的時鐘機制,每秒鐘執(zhí)行大約60次左右,可以使繪圖代碼與視圖的刷新頻率保持同步,而NSTimer無法確保計時器實際被觸發(fā)的準確時間,一個CADisplayLink實例對象,會根據(jù)更新率定時同步繪畫內(nèi)容。需要提供一個選擇器,當更新內(nèi)容的時候。這個需要添加到一個RunLoop當中的去(相當是添加了定時器類型的Runloop)。
具體的使用使用方法:
- 定義CADisplayLink并制定觸發(fā)調(diào)用方法
- 將顯示鏈接添加到主運行循環(huán)隊列
CADisplayLink相關(guān)的屬性有
-
duration:每幀之間的時間 -
frameInterval:就是間隔多少幀調(diào)用一次選擇器。,默認值是1. -
paused: 是否暫停當前的定時器,控制CADisplayLink的運行 -
timestamp:上一幀結(jié)束時候的時間,下一幀需要知道什么內(nèi)容將被呈現(xiàn)
由于 CADisplayLink 綁定的方法會在每次屏幕刷新時被調(diào)用,精確度相當之高。正是基于這個特點,CADisplayLink 非常適合 UI 的重繪。