Core Animation總結

原文作者:60秒熱度
https://juejin.im/post/5cd23f665188252bf01b7a92

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層的主要工作是管理您提供的視覺內容,圖層本身具有可設置的可視屬性,例如背景顏色,邊框和陰影。除了管理視覺內容之外,還保留有關其內容的幾何形狀的信息(例如其位置,大小和變換),用於在屏幕上呈現該內容。

Core Animation

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

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;
}

iOSSir公衆號技術交流微信羣!
需要進羣可以添加公衆號助理“kele22558!”


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章