Core Animation總結

60秒熱度發表於2019-05-08

Core Animation

眾所周知,絢麗動畫效果是iOS系統的一大特點,通過UIView層封裝的動畫,基本可以滿足我們應用開發的所有需求,但若需要更加自由的控制動畫的展示,我們就需要使用CoreAnimation框架中的一些類與方法

Core Animation基礎知識

Core Animation是iOS和OS X上圖形渲染和動畫的基礎結構,可用於為檢視和應用程式的其他可視元素設定動畫。Core Animation的實現邏輯是將大部分實際繪圖工作交給專用圖形硬體加速渲染,以實現高幀率和流暢的動畫,而不會給CPU帶來負擔並降低應用程式的速度。

下圖描述了CoreAnimation與UIKit框架的關係

核心動畫架構圖
Core Animation開發動畫的本質就是將CALayer中的內容轉化為點陣圖從而供硬體操作,所以想熟練掌握動畫操作必須瞭解CALayer

CALayer

CALayer跟UIView概念上很相似,同樣都是被層級管理樹管理的一些矩形塊,同樣可以包含內容,管理子圖層,可以做動畫和變換。但是最大的不同是UIView可以處理使用者的互動,而CALayer是不能夠響應事件的,即使它提供了一些判斷觸點是否在圖層範圍內的方法。每一個UIView檢視內部都封裝了一個CALayer圖層,我們通過UIView的layer屬性訪問這個圖層。其實對於UIView來說負責內容展示的就是它內部的CALayer,UIView只不過是將自身的展示任務交給了內部的CALayer完成,而它還肩負著一些其它的任務,比如說使用者的互動響應,提供一些Core Animation底層方法的高階介面等。

那為什麼不把這些任務放在一個類中處理而是把他們作為平行關係同時存在呢?很重要的原因是要將職責分離,這樣可以避免很多重複的程式碼,由於iOS平臺和MacOS平臺上使用者的互動方式有著本質的不同,在iOS系統中我們使用的是UIKit和UIView,而在MacOS系統中我們使用的是AppKit和NSView,所以在這種情況下將展示部分分離出來會給蘋果的多平臺系統開發帶來便捷。

@interface CALayer : NSObject <NSSecureCoding, CAMediaTiming>
{
@private
  struct _CALayerIvars {
    int32_t refcount;
    uint32_t magic;
    void *layer;
#if TARGET_OS_MAC && !TARGET_RT_64_BIT
    void * _Nonnull unused1[8];
#endif
  } _attr;
}
複製程式碼

CALayer屬性:CALayer層的主要工作是管理您提供的視覺內容,圖層本身具有可設定的可視屬性,例如背景顏色,邊框和陰影。除了管理視覺內容之外,還保留有關其內容的幾何形狀的資訊(例如其位置,大小和變換),用於在螢幕上呈現該內容。

CAlayer屬性圖

Core Animation

接下來我們將講解下Core Animation的CAAnimation、CAPropertyAnimation、CABasicAnimation、CAKeyframeAnimation、CASpringAnimation、CATransition、CAAnimationGroup及他們之間的關係,其間也穿插了CALayer動畫執行的原理、Animation-KeyPath值、CATransaction事務類、檢測動畫的結束、暫停和恢復圖層的動畫等內容

Core Animation類圖

CAAnimation

CAAnimation是核心動畫的基類,不能直接使用,主要負責動畫的時間、速度等,本身實現了CAMediaTiming協議。

@interface CAAnimation : NSObject
    <NSSecureCoding, NSCopying, CAMediaTiming, CAAction>
{
@private
  void *_attr;
  uint32_t _flags;
}
複製程式碼
CAAnimation屬性 說明
timingFunction CAMediaTimingFunction速度控制函式,控制動畫執行的節奏
removedOnCompletion 預設為YES,代表動畫執行完畢後就從圖層上移除,圖形會恢復到動畫執行前的狀態。如果想讓圖層保持顯示動畫執行後的狀態,那就設定為NO,不過還要設定fillMode為kCAFillModeForwards
delegate 代理(animationDidStart、animationDidStop)

ps:CAMediaTimingFunction介紹

kCAMediaTimingFunctionLinear(線性):勻速,給你一個相對靜態的感覺
kCAMediaTimingFunctionEaseIn(漸進):動畫緩慢進入,然後加速離開
kCAMediaTimingFunctionEaseOut(漸出):動畫全速進入,然後減速的到達目的地
kCAMediaTimingFunctionEaseInEaseOut(漸進漸出):動畫緩慢的進入,中間加速,然後減速的到達目的地。這個是預設的動畫行為。
複製程式碼
CAMediaTiming協議

duration,beginTime、repeatCount、speed、timeOffset、repeatDuration、autoreverses這些時間相關的屬性都在這個類中。協議中的這些屬性通過一些方式結合在一起,準確的控制著時間。

CAMediaTiming屬性 說明
beginTime 指定動畫開始的時間。從開始延遲幾秒的話,設定為【CACurrentMediaTime() + 秒數】 的方式
duration 動畫的時長
speed 動畫執行速度(如果把動畫的duration設定為3秒,而speed設定為2,動畫將會在1.5秒結束,因為它以兩倍速在執行)
timeOffset 結合一個暫停動畫(speed=0)一起使用來控制動畫的“當前時間”。暫停的動畫將會在第一幀卡住,然後通過改變timeOffset來隨意控制動畫程式
repeatCount 重複的次數。不停重複設定為 HUGE_VALF
repeatDuration 設定動畫的時間。在該時間內動畫一直執行,不計次數。
autoreverses 動畫結束時是否執行逆動畫,如果duration為1s,則完成一次autoreverse就需要2s。
fillMode CAMediaTimingFillMode列舉

ps:CAMediaTimingFillMode介紹

kCAFillModeRemoved:這個是預設值,也就是說當動畫開始前和動畫結束後,動畫對layer都沒有影響,動畫結束後,layer會恢復到之前的狀態
kCAFillModeForwards:當動畫結束後,layer會一直保持著toValue的狀態
kCAFillModeBackwards:如果要讓動畫在開始之前(延遲的這段時間內)顯示fromValue的狀態
kCAFillModeBoth:這個其實就是上面兩個的合成.動畫加入後開始之前,layer便處於動畫初始狀態,動畫結束後layer保持動畫最後的狀態

注意必須配合animation.removeOnCompletion = NO才能達到以上效果

複製程式碼

CAPropertyAnimation

繼承自CAAnimation,不能直接使用,要想建立動畫物件,應該使用它的兩個子類:CABasicAnimation和CAKeyframeAnimation

You do not create instances of CAPropertyAnimation: to animate the properties of a Core Animation layer, create instance of the concrete subclasses CABasicAnimation or CAKeyframeAnimation.
複製程式碼
CAPropertyAnimation屬性 說明
keyPath 通過指定CALayer的一個屬性名稱為keyPath(NSString型別),並且對CALayer的這個屬性的值進行修改,達到相應的動畫效果。比如,指定@“position”為keyPath,就修改CALayer的position屬性的值,以達到平移的動畫效果

CABasicAnimation

CABasicAnimation是核心動畫類簇中的一個類,其父類是CAPropertyAnimation,其子類是CASpringAnimation,它的祖父是CAAnimation。它主要用於製作比較單一的動畫,例如,平移、縮放、旋轉、顏色漸變、邊框的值的變化等,也就是將layer的某個屬性值從一個值到另一個值的變化

CABasicAnimation屬性 說明
fromValue 所改變屬性的起始值
toValue 所改變屬性的結束時的值
byValue 所改變屬性相同起始值的改變數

程式碼如下

        let baseAnim = CABasicAnimation(keyPath: "position")
        baseAnim.duration = 2;
        //開始的位置
        baseAnim.fromValue = NSValue(cgPoint: (imgView?.layer.position)!)
        baseAnim.toValue = NSValue(cgPoint: CGPoint(x: 260, y: 260))
//        baseAnim.isRemovedOnCompletion = false
//        baseAnim.fillMode = CAMediaTimingFillMode.forwards
        imgView?.layer.add(baseAnim, forKey: "baseAnim-position")
        imgView?.center = CGPoint(x: 260, y: 260)

複製程式碼
防止動畫結束後回到初始狀態

如上面程式碼所示,需要新增imgView?.center = CGPoint(x: 260, y: 260)來防止防止動畫結束後回到初始狀態,網上還有另外一種方法是 設定removedOnCompletion、fillMode兩個屬性

baseAnim.removedOnCompletion = NO;
baseAnim.fillMode = kCAFillModeForwards;
複製程式碼

但是這種方法會造成modelLayer沒有修改,_view1的實際座標點並沒有在所看到的位置,會產生一些問題

CALayer動畫執行的原理

CALayer有兩個例項方法presentationLayer(簡稱P)和modelLayer(簡稱M),

/* presentationLayer
 * 返回一個layer的拷貝,如果有任何活動動畫時,包含當前狀態的所有layer屬性
 * 實際上是逼近當前狀態的近似值。
 * 嘗試以任何方式修改返回的結果都是未定義的。
 * 返回值的sublayers 、mask、superlayer是當前layer的這些屬性的presentationLayer
 */

- (nullable instancetype)presentationLayer;

/* modelLayer
 * 對presentationLayer呼叫,返回當前模型值。
 * 對非presentationLayer呼叫,返回本身。
 * 在生成表示層的事務完成後呼叫此方法的結果未定義。
 */

- (instancetype)modelLayer;
複製程式碼

從中可以看到P即是我們看到的螢幕上展示的狀態,而M就是我們設定完立即生效的真實狀態;打一個比方的話,P是個瞎子,只負責走路(繪製內容),而M是個瘸子,只負責看路(如何繪製)

**CALayer動畫執行的原理:**P會在每次螢幕重新整理時更新狀態,當有動畫CAAnimation(簡稱A)加入時,P由動畫A控制進行繪製,當動畫A結束被移除時P則再去取M的狀態展示。但是由於M沒有變化,所以動畫執行結束又會回到起點。如果想要P在動畫結束後就停在當前狀態而不回到M的狀態,我們就需要給A設定兩個屬性,一個是A.removedOnCompletion = NO;表示動畫結束後A依然影響著P,另一個是A.fillMode = kCAFillModeForwards,這兩行程式碼將會讓A控制住P在動畫結束後保持不變,但是此時我們的P和M不同步,我們看到的P是toValue的狀態,而M則還是自己原來的狀態。舉個例子,我們初始化一個view,它的狀態為1,我們給它的layer加個動畫,from是0,to是2,設定fillMode為kCAFillModeForewards,則動畫結束後P的狀態是2,M的狀態是1,這可能會導致一些問題出現。比如你點P所在的位置點不動,因為響應點選的是M。所以我們應該讓P和M同步,如上程式碼imgView?.center = CGPoint(x: 260, y: 260)需要提一點的是:對M賦值,不會影響P的顯示,當P想要顯示的時候,它已經被A控制了,並不會先閃現一下。

Animation-KeyPath值

上面的動畫的KeyPath值我們只使用了position,其實還有很多型別可以設定,下面我們列出了一些比較常用的

keyPath值 說明 值型別
position 移動位置 CGPoint
opacity 透明度 0-1
bounds 變大與位置 CGRect
bounds.size 由小變大 CGSize
backgroundColor 背景顏色 CGColor
cornerRadius 漸變圓角 任意數值
borderWidth 改變邊框border的大小((圖形周圍邊框,border預設為黑色)) 任意數值
contents 改變layer內容(圖片)注意如果想要達到改變內容的動畫效果;首先在執行動畫之前定義好layer的contents contents CGImage
transform.scale 縮放、放大 0.0-1.0
transform.rotation.x 旋轉動畫(翻轉,沿著X軸) M_PI*n
transform.rotation.Y 旋轉動畫(翻轉,沿著Y軸) M_PI*n
transform.rotation.Z 旋轉動畫(翻轉,沿著Z軸) M_PI*n
transform.translation.x 旋轉動畫(翻轉,沿著X軸) 任意數值
transform.translation.y 旋轉動畫(翻轉,沿著Y軸) 任意數值

CAKeyframeAnimation

CABasicAnimation是將屬性從起始值更改為結束值,而CAKeyframeAnimation物件是允許你以線性或非線性的方式設定一組目標值的動畫。關鍵幀動畫由一組目標資料值和每個值到達的時間組成。不但可以簡單的只指定值陣列和時間陣列,還可以按照路徑進行更改圖層的位置。動畫物件採用您指定的關鍵幀,並通過在給定時間段內從一個值插值到下一個值來構建動畫。

CAKeyframeAnimation屬性 說明
values 關鍵幀值表示動畫必須執行的值,此屬性中的值僅在path屬性的值為nil時才使用。根據屬性的型別,您可能需要用NSValue物件的NSNumber包裝這個陣列中的值。對於一些核心圖形資料型別,您可能還需要將它們轉換為id,然後再將它們新增到陣列中。將給定的關鍵幀值應用於該層的時間取決於動畫時間,由calculationMode、keyTimes和timingFunctions屬性控制。關鍵幀之間的值是使用插值建立的,除非將計算模式設定為kcaanimation離散
path 基於點的屬性的路徑,對於包含CGPoint資料型別的層屬性,您分配給該屬性的路徑物件定義了該屬性在動畫長度上的值。如果指定此屬性的值,則忽略值屬性中的任何資料
keyTimes keyTimes的值與values中的值一一對應指定關鍵幀在動畫中的時間點,取值範圍為[0,1]。當keyTimes沒有設定的時候,各個關鍵幀的時間是平分的
timingFunctions 一個可選的CAMediaTimingFunction物件陣列,指定每個關鍵幀之間的動畫緩衝效果
calculationMode 關鍵幀間插值計算模式
rotationMode 定義沿路徑動畫的物件是否旋轉以匹配路徑切線

ps:

timingFunctions:動畫緩衝效果

kCAMediaTimingFunctionLinear:線性起搏,使動畫在其持續時間內均勻地發生
kCAMediaTimingFunctionEaseIn:使一個動畫開始緩慢,然後加速,隨著它的程式
kCAMediaTimingFunctionEaseOut:使動畫快速開始,然後緩慢地進行
kCAMediaTimingFunctionEaseInEaseOut:使動畫開始緩慢,在其持續時間的中間加速,然後在完成之前再放慢速度
kCAMediaTimingFunctionDefault:預設,確保動畫的時間與大多數系統動畫的匹配
複製程式碼

calculationMode:動畫計算方式

kCAAnimationLinear:預設差值
kCAAnimationDiscrete:逐幀顯示
kCAAnimationPaced:勻速 無視keyTimes和timingFunctions設定
kCAAnimationCubic:keyValue之間曲線平滑 可用 tensionValues,continuityValues,biasValues 調整
kCAAnimationCubicPaced:keyValue之間平滑差值 無視keyTimes

複製程式碼

rotationMode:旋轉方式

kCAAnimationRotateAuto:自動
kCAAnimationRotateAutoReverse:自動翻轉 不設定則不旋轉
複製程式碼

程式碼1、用values屬性

        //建立動畫物件
        let keyAnim = CAKeyframeAnimation(keyPath: "position")
        //設定values
        keyAnim.values = [NSValue(cgPoint: CGPoint(x: 100, y: 200)),
                          NSValue(cgPoint: CGPoint(x: 200, y: 200)),
                          NSValue(cgPoint: CGPoint(x: 200, y: 300)),
                          NSValue(cgPoint: CGPoint(x: 100, y: 300)),
                          NSValue(cgPoint: CGPoint(x: 100, y: 400)),
                          NSValue(cgPoint: CGPoint(x: 200, y: 500))]
        //重複次數 預設為1
        keyAnim.repeatCount = MAXFLOAT
        //設定是否原路返回 預設為false
        keyAnim.autoreverses = true
        //設定移動速度,越小越快
        keyAnim.duration = 4.0
        
        keyAnim.isRemovedOnCompletion = false
        keyAnim.fillMode = .forwards
        
        keyAnim.timingFunctions = [CAMediaTimingFunction(name: .easeInEaseOut)]
        
        imgView?.layer.add(keyAnim, forKey: "keyAnim-Values")
複製程式碼

程式碼2、用path屬性

        //建立動畫物件
        let keyAnim = CAKeyframeAnimation(keyPath: "position")
        
        //建立一個CGPathRef物件,就是動畫的路線
        let path = CGMutablePath()
        //自動沿著弧度移動
        path.addEllipse(in: CGRect(x: 150, y: 200, width: 200, height: 100))
        //設定開始位置
        path.move(to: CGPoint(x: 100, y: 100))
        //沿著直線移動
        path.addLine(to: CGPoint(x: 200, y: 100))
        path.addLine(to: CGPoint(x: 200, y: 200))
        path.addLine(to: CGPoint(x: 100, y: 200))
        path.addLine(to: CGPoint(x: 100, y: 300))
        path.addLine(to: CGPoint(x: 200, y: 400))
        
        //沿著曲線移動
        path.addCurve(to: CGPoint(x: 50.0, y: 275.0), control1: CGPoint(x: 150.0, y: 275.0), control2: CGPoint(x: 70.0, y: 120.0))
        path.addCurve(to: CGPoint(x: 150.0, y: 275.0), control1: CGPoint(x: 250.0, y: 275.0), control2: CGPoint(x: 90.0, y: 120.0))
        path.addCurve(to: CGPoint(x: 250.0, y: 275.0), control1: CGPoint(x: 350.0, y: 275.0), control2: CGPoint(x: 110, y: 120.0))
        path.addCurve(to: CGPoint(x: 350.0, y: 275.0), control1: CGPoint(x: 450.0, y: 275.0), control2: CGPoint(x: 130, y: 120.0))

        keyAnim.path = path
        //重複次數 預設為1
        keyAnim.repeatCount = MAXFLOAT
        //設定是否原路返回 預設為false
        keyAnim.autoreverses = true
        //設定移動速度,越小越快
        keyAnim.duration = 4.0

        keyAnim.isRemovedOnCompletion = false
        keyAnim.fillMode = .forwards
        
        keyAnim.timingFunctions = [CAMediaTimingFunction(name: .easeInEaseOut)]
        
        imgView?.layer.add(keyAnim, forKey: "keyAnim-Path")
        
複製程式碼

CASpringAnimation

iOS9新出的CASpringAnimation繼承於CABaseAnimation,是蘋果專門解決開發者關於彈簧動畫的這個需求而封裝的類

CASpringAnimation屬性 說明
mass 質量,影響圖層運動時的彈簧慣性,質量越大,彈簧拉伸和壓縮的幅度越大,預設值:1
stiffness 剛度係數(勁度係數/彈性係數),剛度係數越大,形變產生的力就越大,運動越快。預設值: 100
damping 阻尼係數,阻止彈簧伸縮的係數,阻尼係數越大,停止越快。預設值:10;
initialVelocity 初始速率,動畫檢視的初始速度大小。預設值:0;速率為正數時,速度方向與運動方向一致,速率為負數時,速度方向與運動方向相反;
settlingDuration 估算時間 返回彈簧動畫到停止時的估算時間,根據當前的動畫引數估算;

程式碼如下

        let springAnim = CASpringAnimation(keyPath: "bounds")

        springAnim.toValue = NSValue(cgRect:CGRect(x: 200, y: 230, width: 140, height: 140))
        
        //mass模擬的是質量,影響圖層運動時的彈簧慣性,質量越大,彈簧拉伸和壓縮的幅度越大 預設值:1
        springAnim.mass = 5
        //stiffness剛度係數(勁度係數/彈性係數),剛度係數越大,形變產生的力就越大,運動越快。預設值: 100
        springAnim.stiffness = 80
        //damping阻尼係數,阻止彈簧伸縮的係數,阻尼係數越大,停止越快。預設值:10;
        springAnim.damping = 8
    //initialVelocity初始速率,動畫檢視的初始速度大小。預設值:0;速率為正數時,速度方向與運動方向一致,速率為負數時,速度方向與運動方向相反;
        springAnim.initialVelocity = 10
        //估算時間 返回彈簧動畫到停止時的估算時間,根據當前的動畫引數估算;
        springAnim.duration = springAnim.settlingDuration
        
        imgView?.layer.add(springAnim, forKey: "springAnim")
複製程式碼

CATransition

CATransition是CAAnimation的子類,用於做轉場動畫,能夠為圖層提供移出螢幕和移入螢幕的動畫效果。

CATransition屬性 說明
type CATransitionType,動畫過渡型別
subtype CATransitionSubtype,動畫移動方向
startProgress 動畫起點(在整體動畫的百分比)
endProgress 動畫終點(在整體動畫的百分比)

ps:如果不需要動畫執行整個過程(動畫執行到中間部分就停止),可以指定startProgress,endProgress屬性。

CATransitionType:動畫過渡型別

kCATransitionFade:漸變
kCATransitionMoveIn:覆蓋
kCATransitionPush:推出
kCATransitionReveal:揭開
複製程式碼

除此之外,還支援以下私有方法

cube:立方體旋轉
suckEffect:吮吸
oglFlip:翻轉
rippleEffect:水波動畫
pageCurl:翻頁
pageUnCurl:反翻頁
cemeraIrisHollowOpen:開鏡頭
cameraIrisHollowClose:關鏡頭

複製程式碼

CATransitionSubtype:控制動畫方向,上/下/左/右 4個不同方向的動畫

kCATransitionFromRight
kCATransitionFromLeft
kCATransitionFromTop
kCATransitionFromBottom
複製程式碼

CAAnimationGroup

單一的動畫並不能滿足某些特定需求,這時就需要用到CAAnimationGroup,其是CAAnimation的子類,預設情況下,一組動畫物件是同時執行的,也可以通過設定動畫物件的beginTime屬性來更改動畫的時間

CATransition屬性 說明
animations [CAAnimation],動畫組

程式碼如下

        let groupAnim = CAAnimationGroup()
        
        //建立keyAnim
        let keyAnim = CAKeyframeAnimation(keyPath: "position")
        //設定values
        keyAnim.values = [NSValue(cgPoint: CGPoint(x: 100, y: 200)),
                          NSValue(cgPoint: CGPoint(x: 200, y: 200)),
                          NSValue(cgPoint: CGPoint(x: 200, y: 300)),
                          NSValue(cgPoint: CGPoint(x: 100, y: 300)),
                          NSValue(cgPoint: CGPoint(x: 100, y: 400)),
                          NSValue(cgPoint: CGPoint(x: 200, y: 500))]
        keyAnim.duration = 4.0
        
        keyAnim.timingFunctions = [CAMediaTimingFunction(name: .easeInEaseOut)]
        
        //建立漸變圓角
        let animation = CABasicAnimation(keyPath: "cornerRadius")
        animation.toValue = 40
        animation.duration = 4.0
        imgView?.layer.masksToBounds = true
        
        groupAnim.animations = [keyAnim, animation]
        groupAnim.duration = 4.0
        groupAnim.repeatCount = MAXFLOAT
        groupAnim.autoreverses = true

        imgView?.layer.add(groupAnim, forKey: "groupAnim")
        
複製程式碼

將動畫分組在一起的更高階方法是使用事務物件。通過允許您建立巢狀的動畫集併為每個動畫分配不同的動畫引數,事務提供了更大的靈活性。

CATransaction事務類

CATransaction事務類可以對多個layer的屬性同時進行修改,它分隱式事務,和顯式事務。 當我們向圖層新增顯式或隱式動畫時,Core Animation都會自動建立隱式事務。但是,我們還可以建立顯式事務以更精確地管理這些動畫。

  • 區分隱式動畫和隱式事務:隱式動畫通過隱式事務實現動畫 。
  • 區分顯式動畫和顯式事務:顯式動畫有多種實現方式,顯式事務是一種實現顯式動畫的方式。
  • 除顯式事務外,任何對於CALayer屬性的修改,都是隱式事務.

隱式事務

//建立layer
let layer = CALayer()
layer.bounds = CGRect(x: 0, y: 0, width: 100, height: 100)
layer.position = CGPoint(x: 100, y: 350)
layer.backgroundColor = UIColor.red.cgColor
layer.borderColor = UIColor.black.cgColor
layer.opacity = 1.0
view.layer.addSublayer(layer)

//觸發動畫

// 設定變化動畫過程是否顯示,預設為true不顯示
CATransaction.setDisableActions(false)
layer.cornerRadius = (layer.cornerRadius == 0.0) ? 30.0 : 0.0
layer.opacity = (layer.opacity == 1.0) ? 0.5 : 1.0


複製程式碼

顯式事務:通過明確的呼叫begin,commit來提交動畫

CATransaction.begin()
layer.zPosition = 200.0
layer.opacity = 0.0
CATransaction.commit()
複製程式碼

使用事務的主要原因之一是在顯式事務的範圍內,我們可以更改持續時間,計時功能和其他引數。還可以為整個事務分配完成塊,以便在動畫組完成時通知應用。

例如,將動畫的預設持續時間更改為8秒,使用setValue:forKey:方法進行修改,目前支援的屬性包括: "animationDuration", "animationTimingFunction","completionBlock", "disableActions".

CATransaction.begin()
CATransaction.setValue(8.0, forKey: "animationDuration")
//執行動畫
CATransaction.commit()
複製程式碼

巢狀事務: 當我們要為不同動畫集提供不同預設值的情況下可以使用巢狀事務。要將一個事務巢狀在另一個事務中,只需再次呼叫begin,且每個begin呼叫必須一一對應一個commit方法。只有在為最外層事務提交更改後,Core Animation才會開始關聯的動畫。

巢狀顯式事務程式碼

//事務巢狀
CATransaction.begin()   // 外部transaction
CATransaction.setValue(2.0, forKey: "animationDuration")
layer.position = CGPoint(x: 140, y: 140)
        
CATransaction.begin()   // 內部transaction
CATransaction.setValue(5.0, forKey: "animationDuration")
layer.zPosition = 200.0
layer.opacity = 0.0
        
CATransaction.commit()  // 內部transaction
CATransaction.commit()  // 外部transaction

複製程式碼

檢測動畫的結束

核心動畫支援檢測動畫開始或結束的時間。這些通知是進行與動畫相關的任何內務處理任務的好時機。例如,您可以使用開始通知來設定一些相關的狀態資訊,並使用相應的結束通知來拆除該狀態。

有兩種不同的方式可以通知動畫的狀態:

  • 使用setCompletionBlock:方法將完成塊新增到當前事務。當事務中的所有動畫完成後,事務將執行完成塊。
  • 將委託分配給CAAnimation物件並實現animationDidStart:animationDidStop:finished:委託方法。

如果要讓兩個動畫連結在一起,以便在另一個完成時啟動,請不要使用動畫通知。而是使用動畫物件的beginTime屬性按照所需的時間啟動每個動畫物件。將兩個動畫連結在一起,只需將第二個動畫的開始時間設定為第一個動畫的結束時間。

每個圖層都有自己的本地時間,用於管理動畫計時。通常,兩個不同層的本地時間足夠接近,您可以為每個層指定相同的時間值,使用者可能不會注意到任何內容。但是由於superLayer或其本身Layer的時序引數設定,層的本地時間會發生變化。例如,更改Layer的speed屬性會導致該Layer(及其子Layer)上的動畫持續時間按比例更改。

為了確保Layer的時間值合適,CALayer類定義了convertTime:fromLayer:convertTime:toLayer:方法。我們可以使用這些方法將固定時間值轉換為Layer的本地時間或將時間值從一個Layer轉換為另一個Layer。這些方法可能影響圖層本地時間的媒體計時屬性,並返回可與其他圖層一起使用的值。

可使用下面示例來獲取圖層的當前本地時間。CACurrentMediaTime函式返回計算機的當前時鐘時間,該方法將本機時間並轉換為圖層的本地時間。

獲取圖層的當前本地時間

CFTimeInterval localLayerTime = [myLayer convertTime:CACurrentMediaTime()fromLayer:nil];
複製程式碼

在圖層的本地時間中有時間值後,可以使用該值更新動畫物件或圖層的與時序相關的屬性。使用這些計時屬性,您可以實現一些有趣的動畫行為,包括:

  • beginTime屬性設定動畫的開始時間。通常動畫開始下一個週期的時候,我們可以使用beginTime將動畫開始時間延遲幾秒鐘。將兩個動畫連結在一起的方法是將一個動畫的開始時間設定為與另一個動畫的結束時間相匹配。如果延遲動畫的開始,則可能還需要將fillMode屬性設定為kCAFillModeBackwards。即使圖層樹中的圖層物件包含不同的值,此填充模式也會使圖層顯示動畫的起始值。如果沒有此填充模式,您將看到在動畫開始執行之前跳轉到最終值。其他填充模式也可用。

  • autoreverses屬性使動畫在指定時間內執行,然後返回到動畫的起始值。我們可以將autoreverses與repeatCount組合使用,就可以起始值和結束值之間來回動畫。將重複計數設定為自動迴轉動畫的整數(例如1.0)會導致動畫停止在其起始值上。新增額外的半步(例如重複計數為1.5)會導致動畫停止在其結束值上。 使用timeOffset具有組動畫的屬性可以在稍後的時間啟動某些動畫。

暫停和恢復圖層的動畫

- (void)pauseLayer:(CALayer *)layer {
   CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime()fromLayer:nil];
   layer.speed = 0.0;
   layer.timeOffset = pausedTime;
}
 
- (void)resumeLayer:(CALayer *)layer {
   CFTimeInterval pausedTime = [layer timeOffset];
   layer.speed = 1.0;
   layer.timeOffset = 0.0;
   layer.beginTime = 0.0;
   CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime()fromLayer:nil]  -  pausedTime;
   layer.beginTime = timeSincePause;
}
複製程式碼

程式碼及參考資料

github原始碼地址

參考資料:

核心動畫-官方文件

iOS CALayer圖層漫談(一)

Core Animation系列之CATransaction

iOS動畫篇_CoreAnimation(超詳細解析核心動畫)

iOS動畫,原理與實操

iOS動畫全面解析

iOS CoreAnimation專題——原理篇(三) CALayer的模型層與展示層

相關文章