1. 程式人生 > >iOS出門必備之CoreAnimation(核心動畫)

iOS出門必備之CoreAnimation(核心動畫)

CoreAnimation

  • 前段時間接觸到了一個牛逼的動畫框架POP,本來想來裝裝逼,突然發現,蘋果大大的CoreAnimation我還不會用呢!
  • 依稀記得喬幫主在2007年的WWDC大會上親自為你演示Core Animation的強大:點選檢視視訊(不好意思,又裝逼了)
  • 言歸正傳,我只是來溫習一下CoreAnimation,還望路過的大神不要吐槽我太low

一、Core Animation簡介

  • Core Animation,中文翻譯為核心動畫,它是一組非常強大的動畫處理API,使用它能做出非常炫麗的動畫效果,而且往往是事半功倍。也就是說,使用少量的程式碼就可以實現非常強大的功能。
  • Core Animation可以用在Mac OS X和iOS平臺。
  • Core Animation的動畫執行過程都是在後臺操作的,不會阻塞主執行緒。
  • 要注意的是,Core Animation是直接作用在CALayer上的,並非UIView
  • 通過呼叫CALayer的addAnimation:forKey:方法增加CAAnimation物件到CALayer中,這樣就能開始執行動畫了
  • 通過呼叫CALayer的removeAnimationForKey:方法可以停止CALayer中的動畫

二. Core Animation及其相關屬性

  • 要想執行動畫,就必須初始化一個CAAnimation物件。
  • 一般情況下,我們使用的比較多的是CAAnimation的子類,因此,先大致看看CAAnimation的繼承結構
  • 黑線代表繼承,黑色文字代表類名,白色文字代表屬性。其中CAMediaTiming是一個協議(protocol)

Core Animation結構劃分.png

1.需要注意的是

  • CAAnimation是所有動畫類的父類,但是它不能直接使用,應該使用它的子類
  • CAPropertyAnimation也是不能直接使用的,也要使用它的子類
  • 能用的動畫類只剩下4個:CABasicAnimation、CAKeyframeAnimation、CATransition、CAAnimationGroup

2.常用屬性

1). removedOnCompletion:預設為true,代表動畫執行完畢後就從圖層上移除
- 圖形會恢復到動畫執行前的狀態。如果想讓圖層保持顯示動畫執行後的狀態,那就設定為false,不過還要設定fillModekCAFillModeForwards

2). timingFunction:控制動畫執行的節奏

/** timingFunction可選的值 **/
@available(iOS 2.0, *)
public let kCAMediaTimingFunctionLinear: String
//1.(勻速): 在整個動畫時間內動畫都是以一個相同的速度來改變

@available(iOS 2.0, *)
public let kCAMediaTimingFunctionEaseIn: String
//2. (漸進): 緩慢進入, 加速離開

@available(iOS 2.0, *)
public let kCAMediaTimingFunctionEaseOut: String
//3. (漸出): 快速進入, 減速離開

@available(iOS 2.0, *)
public let kCAMediaTimingFunctionEaseInEaseOut: String
//4. (漸進漸出): 緩慢進入, 中間加速, 減速離開

@available(iOS 3.0, *)
public let kCAMediaTimingFunctionDefault: String
//5. (預設): 效果基本等同於EaseOut(漸出)

3). fillMode決定當前物件在非active時間段的行為。
- 要想fillMode有效,需設定removedOnCompletion = false
- fillMode可選的值

/* `fillMode' options. */
@available(iOS 2.0, *)
public let kCAFillModeForwards: String
//1. 當動畫結束後,layer會一直保持著動畫最後的狀態

@available(iOS 2.0, *)
public let kCAFillModeBackwards: String
//2. 設定為該值,將會立即執行動畫的第一幀,不論是否設定了 beginTime屬性。觀察發現,設定該值,剛開始檢視不見,還不知道應用在哪裡

@available(iOS 2.0, *)
public let kCAFillModeBoth: String
//3. 該值是 kCAFillModeForwards 和 kCAFillModeBackwards的組合狀態; 動畫加入後開始之前,layer便處於動畫初始狀態,動畫結束後layer保持動畫最後的狀態

@available(iOS 2.0, *)
public let kCAFillModeRemoved: String
//4. 預設值,動畫將在設定的 beginTime 開始執行(如沒有設定beginTime屬性,則動畫立即執行),動畫執行完成後會將layer的改變恢復原狀

4). delegate:動畫代理,用來監聽動畫的執行過程


public protocol CAAnimationDelegate : NSObjectProtocol {

    // 動畫開始執行的時候觸發這個方法
    @available(iOS 2.0, *)
    optional public func animationDidStart(_ anim: CAAnimation)

    // 動畫執行完畢的時候觸發這個方法
    @available(iOS 2.0, *)
    optional public func animationDidStop(_ anim: CAAnimation, finished flag: Bool)
}

5). 其他相關屬性

duration    動畫的時長
repeatCount    重複的次數。不停重複設定為 HUGE_VALF
repeatDuration    設定動畫的時間。在該時間內動畫一直執行,不計次數。
beginTime    指定動畫開始的時間。從開始延遲幾秒的話,設定為【CACurrentMediaTime() + 秒數】 的方式
timingFunction    設定動畫的速度變化
autoreverses    動畫結束時是否執行逆動畫
fromValue    所改變屬性的起始值(Swift中為Any型別,OC中要包裝成NSValue物件)
toValue    所改變屬性的結束時的值(型別與fromValue相同)
byValue    所改變屬性相同起始值的改變數(型別與fromValue相同)

三. CABasicAnimation

  • CABasicAnimationCAPropertyAnimation的子類,使用它可以實現一些基本的動畫效果,它可以讓CALayer的某個屬性從某個值漸變到另一個值。下面就用CABasicAnimation實現幾個簡單的動畫

1. 平移動畫

方法一: 改變label的position

let caBasic = CABasicAnimation(keyPath: "position")
caBasic.duration = 2
caBasic.fromValue = redLabel.layer.position
caBasic.toValue = CGPoint(x: kScreenWidth - 50, y: 200)
caBasic.delegate = self
caBasic.isRemovedOnCompletion = false
caBasic.fillMode = kCAFillModeForwards
redLabel.layer.add(caBasic, forKey: "redLabel1")
  • 初始化方法中是”position”,說明要修改的是CALayer的position屬性,也就是會執行平移動畫
  • 預設情況下,動畫執行完畢後,動畫會自動從CALayer上移除,CALayer又會回到原來的狀態。為了保持動畫執行後的狀態,可以加入第6、7行程式碼
  • 第8行後面的@”redLabel1”是給動畫物件起個名稱,以後可以呼叫CALayer的removeAnimationForKey:方法根據動畫名稱停止相應的動畫
  • 遵循的代理方法
extension ViewController: CAAnimationDelegate {
    //開始執行
    func animationDidStart(_ anim: CAAnimation) {
        print("開始動畫--layer:", redLabel.layer.position)
    }
    //結束之行
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        print("結束動畫--layer:", redLabel.layer.position)
    }
}


//列印結果為:
//開始動畫--layer: (35.0, 213.0)
//結束動畫--layer: (35.0, 213.0)

從列印資訊可以看出,實際上,動畫執行完畢後,並沒有真正改變CALayer的position屬性的值!

方法二.

 let basic = CABasicAnimation(keyPath: "transform")
basic.duration = 2
let form = CATransform3DMakeTranslation(350, 400, 0)
basic.toValue = form
blueLabel.layer.add(basic, forKey: "blueLabel")

2. 旋轉動畫

let basic1 = CABasicAnimation(keyPath: "transform")
basic1.duration = 1
basic1.toValue = CATransform3DMakeRotation(0.25, 0, 0, 1)
basic1.isRemovedOnCompletion = false
basic1.fillMode = kCAFillModeForwards
blueLabel.layer.add(basic1, forKey: "basic1")
  • 可以不用設定fromValue,這裡只設置了toValue

3. 縮放動畫

  • CALayer的寬度從0.5倍變為2倍
  • CALayer的高度從0.5倍變為1.5倍
let basic1 = CABasicAnimation(keyPath: "transform")
basic1.duration = 1
basic1.toValue = CATransform3DMakeScale(0.5, 0.5, 1)
basic1.toValue = CATransform3DMakeScale(2, 1.5, 1)
basic1.isRemovedOnCompletion = false
basic1.fillMode = kCAFillModeForwards
blueLabel.layer.add(basic1, forKey: "basic1")
  • CABasicAnimation雖然能夠做很多基本的動畫效果,但是有個侷限性,只能讓CALayer的屬性從某個值漸變到另一個值,僅僅是在2個值之間漸變
  • 總結一些常用的animationKeyPath值的
說明 使用形式
transform.scale 比例轉化 0.5
transform.scale.x 寬的比例 0.5
transform.rotation.x 圍繞x軸旋轉 @(M_PI_4)(OC), 0.25(Swift)
cornerRadius 圓角的設定 30
backgroundColor 背景顏色的變化 UIColor.purpleColor.cgColor
bounds 大小,中心不變 CGRect
position 位置(中心點的改變) CGPoint
contents 內容,比如UIImageView的圖片 imageAnima.toValue = UIImage(named: “toImage”)?.cgImage
opacity 透明度 0.7
contentsRect.size.width 橫向拉伸縮放 最好是0~1之間的

四. CAKeyframeAnimation——關鍵幀動畫

  • 關鍵幀動畫,也是CAPropertyAnimation的子類,與CABasicAnimation的區別是:
    • CABasicAnimation只能從一個數值(fromValue)變到另一個數值(toValue)
    • CAKeyframeAnimation會使用一個Array儲存這些數值
  • 屬性說明:
    • values:上述的Array物件。裡面的元素稱為“關鍵幀”(keyframe)。動畫物件會在指定的時間(duration)內,依次顯示values陣列中的每一個關鍵幀
    • path:可以設定一個CGPathRef、CGMutablePathRef,讓圖層按照路徑軌跡移動。path只對CALayer的anchorPointposition起作用。如果設定了path,那麼values將被忽略
    • keyTimes:可以為對應的關鍵幀指定對應的時間點,其取值範圍為0到1.0,keyTimes中的每一個時間值都對應values中的每一幀。如果沒有設定keyTimes,各個關鍵幀的時間是平分的
    • calculationMode: 該屬性決定了物體在每個子路徑下是跳著走還是勻速走,跟timeFunctions屬性有點類似
    • kCAAnimationLinear預設值,表示當關鍵幀為座標點的時候,關鍵幀之間直接直線相連進行插值計算;
    • kCAAnimationDiscrete 離散的,就是不進行插值計算,所有關鍵幀直接逐個進行顯示;
    • kCAAnimationPaced 使得動畫均勻進行,而不是按keyTimes設定的或者按關鍵幀平分時間,此時keyTimes和timingFunctions無效;</li>
      <li>
      kCAAnimationCubic對關鍵幀為座標點的關鍵幀進行圓滑曲線相連後插值計算,對於曲線的形狀還可以通過tensionValues,continuityValues,biasValues來進行調整自定義主要目的是使得執行的軌跡變得圓滑;</li>
      <li>
      kCAAnimationCubicPaced看這個名字就知道和kCAAnimationCubic有一定聯絡,其實就是在kCAAnimationCubic的基礎上使得動畫執行變得均勻,就是系統時間內運動的距離相同,此時keyTimes以及timingFunctions`也是無效的.
  • CABasicAnimation可看做是隻有2個關鍵幀的CAKeyframeAnimation

values方式

let key = CAKeyframeAnimation(keyPath: "position")
key.duration = 3
key.repeatCount = HUGE //無線迴圈
key.calculationMode = kCAAnimationPaced
key.values = [redLabel.frame.origin, CGPoint(x: 180, y: 70), CGPoint(x: 180, y: 200), redLabel.frame.origin]
key.keyTimes = [NSNumber(value: 0.0), NSNumber(value: 0.6), NSNumber(value: 0.7), NSNumber(value: 0.8)]
redLabel.layer.add(key, forKey: "key")

五. CASpringAnimation

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

1. CASpringAnimation相關屬性

//1. 質量,影響圖層運動時的彈簧慣性,質量越大,彈簧拉伸和壓縮的幅度越大, 預設值: 1
open var mass: CGFloat

//2. 剛度係數(勁度係數/彈性係數),剛度係數越大,形變產生的力就越大,運動越快(預設值: 100)
open var stiffness: CGFloat

//3. 阻尼係數,阻止彈簧伸縮的係數,阻尼係數越大,停止越快(預設值: 10)
open var damping: CGFloat

//4. 初始速率,動畫檢視的初始速度大小, 預設0
//速率為正數時,速度方向與運動方向一致,速率為負數時,速度方向與運動方向相反(預設值: 0)
open var initialVelocity: CGFloat

//5. 估算時間 返回彈簧動畫到停止時的估算時間,根據當前的動畫引數估算(只讀)
open var settlingDuration: CFTimeInterval { get }

2. 示例程式碼

let spring = CASpringAnimation(keyPath: "position.y")
spring.mass = 5
spring.stiffness = 100
spring.damping = 5
spring.initialVelocity = 2
spring.fromValue = blueLabel.layer.position.y
spring.toValue = kScreenHeight - 150
spring.duration = spring.settlingDuration
blueLabel.layer.add(spring, forKey: "spring")

六. CAAnimationGroup動畫組

  • 是CAAnimation的子類,可以儲存一組動畫物件,將CAAnimationGroup物件加入層後,組中所有動畫物件可以同時併發執行
  • 屬性說明:
    • animations:用來儲存一組動畫物件的Array
  • 預設情況下,一組動畫物件是同時執行的,也可以通過設定動畫物件的beginTime屬性來更改動畫的開始時間

程式碼示例:

  • 同時執行:平移、縮放、位移動畫 -> 使用動畫組
    //動畫組
    fileprivate func getCAAnimationGroup(){
        //0. 初始化動畫組
        let group = CAAnimationGroup()

        //1. 平移動畫
        let basic1 = CABasicAnimation(keyPath: "position")
        basic1.fromValue = blueLabel.layer.position
        basic1.toValue = CGPoint(x: CGFloat(arc4random_uniform(200)), y: CGFloat(arc4random_uniform(500)))

        //2. 縮放動畫
        let basic2 = CABasicAnimation(keyPath: "transform.scale")
        var scale: CGFloat = 0.1
        scale = scale < 1 ? 1.5 : 0.5
        basic2.toValue = scale

        //3. 旋轉動畫
        let basic3 = CABasicAnimation(keyPath: "transform.rotation")
        basic3.toValue = CGFloat(arc4random_uniform(360)) / 180.0

        //4. 新增到動畫組
        group.animations = [basic1, basic2, basic3]
        //取消反彈
        group.isRemovedOnCompletion = false
        group.fillMode = kCAFillModeForwards
        group.duration = 0.5
        blueLabel.layer.add(group, forKey: "group")
    }

七. 轉場動畫——CATransition

  • CATransitionCAAnimation的子類,用於做轉場動畫,能夠為layer層提供移出螢幕和移入螢幕的動畫效果。
  • iOS比Mac OS X的轉場動畫效果少一點
    UINavigationController就是通過CATransition實現了將控制器的檢視推入螢幕的動畫效果
  • 動畫屬性:
    • type:動畫過渡型別
    • subtype:動畫過渡方向
    • startProgress:動畫起點(在整體動畫的百分比)
    • endProgress:動畫終點(在整體動畫的百分比)

1. typesubtype屬性說明

/* type型別 */
@available(iOS 2.0, *)
public let kCATransitionFade: String
//交叉淡化過渡

@available(iOS 2.0, *)
public let kCATransitionMoveIn: String
//新檢視移到舊檢視上面

@available(iOS 2.0, *)
public let kCATransitionPush: String
//新檢視把舊檢視推出去

@available(iOS 2.0, *)
public let kCATransitionReveal: String
//將舊檢視移開,顯示下面的新檢視


/* subtypes型別 */
@available(iOS 2.0, *)
public let kCATransitionFromRight: String
//從右側轉場

@available(iOS 2.0, *)
public let kCATransitionFromLeft: String
//從左側轉場

@available(iOS 2.0, *)
public let kCATransitionFromTop: String
//從上部轉場

@available(iOS 2.0, *)
public let kCATransitionFromBottom: String
//從底部轉場

注意:
- 除了上述四種效果之外,還有很多私有API效果,使用的時候要小心,可能會導致app稽核不被通過
- 使用的時候要以字串的形式

cube     //立方體翻滾效果
oglFlip  //上下左右翻轉效果
suckEffect   //收縮效果,如一塊布被抽走(不支援過渡方向)
rippleEffect //滴水效果(不支援過渡方向)
pageCurl     //向上翻頁效果
pageUnCurl   //向下翻頁效果
cameraIrisHollowOpen  //相機鏡頭開啟效果(不支援過渡方向)
cameraIrisHollowClose //相機鏡頭關上效果(不支援過渡方向)

效果參考

各引數動畫效果.png

2. 程式碼示例:

  • 展示立方體翻滾效果的圖片瀏覽

1. 初始化變數

//初始化變數
fileprivate var imageView = UIImageView(frame: UIScreen.main.bounds)
fileprivate var currentIndex = 0

2. 需要在viewDidLoad中呼叫一下方法

//轉場動畫
    fileprivate func imageCATransition(){
        //0.初始化ImageView
        imageView.isUserInteractionEnabled = true
        imageView.image = UIImage(named: "0.jpg")
        view.addSubview(imageView)

        //1. 新增滑動手勢
        let left = UISwipeGestureRecognizer(target: self, action: #selector(leftSwipe(gesture:)))
        left.direction = .left
        imageView.addGestureRecognizer(left)
        let right = UISwipeGestureRecognizer(target: self, action: #selector(rightSwipe(gesture:)))
        right.direction = .right
        imageView.addGestureRecognizer(right)
    }

3. 滑動後執行的方法

    //MARK: 手勢相關方法
    //左滑
    @objc fileprivate func leftSwipe(gesture: UIGestureRecognizer) {
        print("左滑動")
        transitionAnimation(isNext: true)
    }
    //右滑
    @objc fileprivate func rightSwipe(gesture: UIGestureRecognizer) {
        print("右滑動")
        transitionAnimation(isNext: false)
    }

    //設定轉場動畫
    fileprivate func transitionAnimation(isNext: Bool){
        let transition = CATransition()
        transition.type = kCATransitionFade
        transition.subtype = isNext ? kCATransitionFromRight : kCATransitionFromLeft
        transition.duration = 1
        imageView.image = getImage(isNext)
        imageView.layer.add(transition, forKey: "transition")
    }

    //獲取下/上一張圖片
    fileprivate func getImage(_ isNext: Bool) -> UIImage {
        currentIndex = isNext ? currentIndex + 1 : currentIndex - 1
        currentIndex = currentIndex < 0 ? 7 : currentIndex
        currentIndex = currentIndex > 7 ? 0 : currentIndex
        return UIImage(named: "\(currentIndex)" + ".jpg")!
    }

八. 總結

  • 核心動畫給我們展示的只是一個假象,layer的的frame、bounds、position並不會在動畫完畢之後發生改變。
  • UIView封裝的動畫,會使會真實修改view的一些屬性
  • 以上就是小編總結的關於Core Animation核心動畫的相關分類
  • 總結的知識點比較簡單, 個人感覺有點low
  • 如有不足之處,還望路過的大神多多指教