iOS 動畫

王曉鵬發表於2019-01-16

iOS 動畫

Git程式碼地址

enter image description here

在iOS實際開發中常用的動畫總結下來包含3種:

  • UIViewAnimation動畫
  • CoreAnimation核心動畫
  • 其他動畫

[TOC]

##UIViewAnimation動畫 UIView的兩種動畫api包含 方法形式block 形式, 其中包含三種動畫實現

  • UIView(UIViewAnimation)
  • UIView (UIViewKeyframeAnimation)
  • UIViewControllerAnimatedTransitioning

###函式形式

// 開始動畫
UIView.beginAnimations("Identifier", context: nil)
//  設定動畫代理
UIView.setAnimationDelegate(self)
// 通過 #selector 選擇器 新增開始動畫方法
UIView.setAnimationWillStart(#selector(animationAction))
// 通過 #selector 選擇器 新增結束動畫方法
UIView.setAnimationDidStop(#selector(animationAction))
// 設定動畫時間間隔
UIView.setAnimationDuration(1.0)
// 設定動畫延遲
UIView.setAnimationDelay(0)
// 設定動畫開始的時間,預設是現在開始
UIView.setAnimationStart(Date())
// 設定動畫曲線
UIView.setAnimationCurve(.easeInOut)
// 設定動畫重複次數,預設是 0
UIView.setAnimationRepeatCount(0) // 0 無線迴圈
// 自動返回原始狀態
UIView.setAnimationRepeatAutoreverses(false) // default = NO. used if repeat count is non-zero
// 設定動畫的開始是從現在的狀態開始, 預設是 false
UIView.setAnimationBeginsFromCurrentState(false)
// 用來開啟或禁止動畫顯示
UIView.setAnimationsEnabled(true)
// 設定動畫的過渡效果
UIView.setAnimationTransition(.curlUp, for: redView, cache: false)

// 設定 UIView 的動畫屬性
redView.transform = CGAffineTransform(rotationAngle: 90)
redView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
redView.transform = CGAffineTransform(translationX: 0, y: 200)

// 動畫的狀態
print(UIView.areAnimationsEnabled)
// 標誌動畫程式碼結束,程式會建立新的執行緒,並準備執行動畫
UIView.commitAnimations()
複製程式碼
UIView.beginAnimations("Identifier", context: nil)
UIView.setAnimationDuration(1)
objectView.center.x = objectView.center.x + 100
UIView.commitAnimations()
複製程式碼

###block形式 將動畫實現封裝在block區域,引數構建在類方法上。

UIView.animate(withDuration: TimeInterval,
                 animations: ()->Void)

UIView.animate(withDuration: TimeInterval,
                 animations: ()->Void,
                 completion: ()->Void)

// 帶動畫曲線動畫
UIView.animate(withDuration: TimeInterval,
                      delay: TimeInterval,
                    options: UIViewAnimationOptions,
                 animations: ()->Void,
                 completion: (()->Void)?)

// 帶彈性動畫
UIView.animate(withDuration: TimeInterval,
                      delay: TimeInterval,
     usingSpringWithDamping: 0,
      initialSpringVelocity: 0,
                    options: UIViewAnimationOptions,
                 animations: ()->Void,
                 completion: (()->Void)?)
複製程式碼
UIView.animate(withDuration: 1.0) {
    self.objectView.center.x = self.objectView.center.x - 100
}
複製程式碼

更多引數動畫函式

//.curveEaseIn, .autoreverse, .repeat
 UIView.animate(withDuration: 1.0, delay: 0, options: [.curveEaseIn, .autoreverse, .repeat], animations: {
     self.objectView.center.x = self.objectView.center.x - 100
 }, completion: nil)
 
 //delay
 UIView.animate(withDuration: 1.0, delay: 0.5, options: .curveEaseIn, animations: {
     self.objectView2.center.x = self.objectView2.center.x - 100
 }, completion: nil)
複製程式碼

各個引數的含義

Duration  :動畫執行的時長
delay     :延時時長,即上一個動畫執行完以後多久再執行下一個動畫
options   :一些樣式的選取
animations:我們想要實現的動畫效果
completion:動畫執行完我們還想做的事情
複製程式碼

options

1.屬性設定

UIViewAnimationOptionLayoutSubviews // 動畫過程中保證子檢視跟隨運動 
UIViewAnimationOptionAllowUserInteraction   // 動畫過程中允許使用者互動  
UIViewAnimationOptionBeginFromCurrentState    // 所有檢視從當前狀態開始執行
UIViewAnimationOptionRepeat  // 重複執行動畫                
UIViewAnimationOptionAutoreverse  // 動畫執行到結束點後仍然以動畫方式回到初始點       
UIViewAnimationOptionOverrideInheritedDuration // 忽略巢狀動畫時間設定
UIViewAnimationOptionOverrideInheritedCurve   // 忽略巢狀動畫速度設定 
UIViewAnimationOptionAllowAnimatedContent  // 動畫過程中重繪檢視(注意僅僅適用於轉場動畫)    
UIViewAnimationOptionShowHideTransitionViews // 檢視切換時直接隱藏舊檢視、顯示新檢視,而不是將舊檢視從父檢視移除(僅僅適用於轉場動畫)  
UIViewAnimationOptionOverrideInheritedOptions //不繼承父動畫設定或動畫型別
複製程式碼

2.動畫速度控制

UIViewAnimationOptionCurveEaseInOut   // 動畫先緩慢,然後逐漸加速
UIViewAnimationOptionCurveEaseIn    // 動畫逐漸變慢        
UIViewAnimationOptionCurveEaseOut   // 動畫逐漸加速       
UIViewAnimationOptionCurveLinear    // 動畫勻速執行,預設值
複製程式碼

3.轉場型別(僅適用於轉場動畫設定,可以從中選擇一個進行設定,基本動畫、關鍵幀動畫不需要設定)

UIViewAnimationOptionTransitionNone  // 沒有轉場動畫效果        
UIViewAnimationOptionTransitionFlipFromLeft  // 從左側翻轉效果 
UIViewAnimationOptionTransitionFlipFromRight  // 從右側翻轉效果
UIViewAnimationOptionTransitionCurlUp // 向後翻頁的動畫過渡效果        
UIViewAnimationOptionTransitionCurlDown  // 向前翻頁的動畫過渡效果      
UIViewAnimationOptionTransitionCrossDissolve  // 舊檢視溶解消失顯示下一個新檢視的效果 
UIViewAnimationOptionTransitionFlipFromTop  // 從上方翻轉效果   
UIViewAnimationOptionTransitionFlipFromBottom // 從底部翻轉效果
複製程式碼

支援的動畫屬性

frame //大小變化:改變檢視框架(frame)和邊界。
bounds //拉伸變化:改變檢視內容的延展區域。
center //居中顯示
transform //仿射變換(transform)
alpha //改變透明度:改變檢視的alpha值。
backgroundColor //改變背景顏色
contentStretch //拉伸內容
複製程式碼
  • Opacity -- 設定透明度
UIView.animate(withDuration: 1.0) {
    self.objectView.alpha = 0.2
}
複製程式碼
  • Scale -- 放大縮小
UIView.animate(withDuration: 1.0) {
    self.objectView.transform = CGAffineTransform(scaleX: 2, y: 2)
}

UIView.animate(withDuration: 1.0, delay: 1.0, options: .curveLinear, animations: {
    self.objectView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
}, completion: nil)
複製程式碼
  • Translation -- 平移
UIView.animate(withDuration: 1.0) {
    self.objectView.transform = CGAffineTransform(translationX: 200, y: 200)
}
複製程式碼
  • Color -- 顏色
UIView.animate(withDuration: 1.0, delay: 0, options: [.autoreverse, .repeat], animations: {
    self.objectView.backgroundColor = UIColor.brown
}, completion: nil)
複製程式碼
  • Rotation -- 旋轉
UIView.animate(withDuration: 1.0, delay: 0, options: [.curveLinear, .repeat], animations: {
    self.objectView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
}, completion: nil)
複製程式碼

###彈簧動畫函式

UIView.animate(withDuration: 1.0, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 20, options: .curveEaseIn, animations: {
    self.objectView.center.y = self.objectView.center.y + 100
}, completion:nil)
複製程式碼

關鍵幀動畫

為當前檢視建立可以容納一個或多個關鍵幀動畫物件的動畫塊,然後根據指定的時間一幀幀的執行指定動畫,需要與addKeyframeWithRelativeStartTime: relativeDuration: animations: 結合使用,注意:如果在block中沒用新增關鍵幀動畫物件,動畫還是會執行,只不過跟呼叫animateWithDuration(duration: delay: options: animations: completion: 效果一樣!簡單點來說,呼叫該API就是建立了一個動畫容器,然後可以向這個容器中新增多個動畫!

UIViewKeyframeAnimationOptions:

1 .CalculationModeLinear:在幀動畫之間採用線性過渡
2 .CalculationModeDiscrete:在幀動畫之間不過渡,直接執行各自動畫
3 .CalculationModePaced:將不同幀動畫的效果儘量融合為一個比較流暢的動畫
4 .CalculationModeCubic:不同幀動畫之間採用Catmull-Rom演算法過渡
5 .CalculationModeCubicPaced:3和4結合,試了就知道什麼效果了
複製程式碼

animateKeyframesWithDuration:delay:options:animations:completion:結合使用,用來指定幀動畫開始時間,持續時間和執行操作,呼叫一次就可以新增一個幀動畫!

frameStartTime:幀動畫開始時間,取值範圍為(0,1),開始時間是相對於整個動畫時間,整個關鍵幀動畫時長6秒,設定開始時間為0.5,那麼這一幀動畫的實際開始時間為第3秒!
2 frameDuration:幀動畫持續時間,取值範圍為(0,1),持續時間也是相對於整個動畫時間,演算法同上!
複製程式碼
UIView.animateKeyframes(withDuration: 10, delay: 0, options: .calculationModeCubicPaced, animations: {
     UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/5, animations: {
         self.objectView.backgroundColor = UIColor.red
     })
     UIView.addKeyframe(withRelativeStartTime: 1/5, relativeDuration: 1/5, animations: {
         self.objectView.backgroundColor = UIColor.green
     })
     UIView.addKeyframe(withRelativeStartTime: 2/5, relativeDuration: 1/5, animations: {
         self.objectView.backgroundColor = UIColor.yellow
     })
     UIView.addKeyframe(withRelativeStartTime: 3/5, relativeDuration: 1/5, animations: {
         self.objectView.backgroundColor = UIColor.purple
     })
     UIView.addKeyframe(withRelativeStartTime: 4/5, relativeDuration: 1/5, animations: {
         self.objectView.backgroundColor = UIColor.gray
     })
 }, completion: nil)
複製程式碼

Transitions 過渡

過度動畫強調的是view改變內容。一般有兩個方法

UIView.transition(with:, duration:, options:, animations:, completion:)
UIView.transition(from: , to:, duration:, options:, completion:)
複製程式碼

過渡動畫的型別是一個options (UIViewAnimationOptions)

.transitionFlipFromLeft,.transitionFlipFromRight
.transitionFlipFromTop,.transitionFlipFromBottom
.transitionCurlUp,.transitionCurlDown
.transitionCrossDissolve
複製程式碼
if self.objectView.backgroundColor == UIColor.gray {
    self.objectView.backgroundColor = UIColor.blue
}else {
    self.objectView.backgroundColor = UIColor.gray
}
}, completion: nil)
複製程式碼

ImageView動畫

在 UIImageView 上執行動畫非常簡單,只需要提供animationImages 屬性。一個UIImage 陣列。這個陣列程式碼一個一個的幀,當我們呼叫 startAnimating 方法的時候,這個陣列的圖片就會輪流播放。animationDuration 決定了播放的速度。animationRepeatCount指定重複次數 (預設是0 , 代表無限重複),或者呼叫stopAnimating 方法停止動畫。

UIImage 有一些類方法為 UIImageView 構造 可以動畫的image :

直接指定了image陣列和duration。

UIImage.animatedImage(with:, duration:)
複製程式碼

提供一個單個的image name , 系統會自動在後面加 "0" (如果失敗則"1") 。使這個image成為第一個image。最後一位數字累加。(知道沒有圖片或者到達”1024“)

UIImage.animatedImageNamed(, duration: )
複製程式碼

跟上面的方式差不多,但是同時對每個image做了拉伸或者平鋪。 影象本身也有resizableImage(withCapInsets: , resizingMode: )方法可以縮放(指定某個區域的拉伸或者平鋪)

UIImage.animatedResizableImageNamed(, capInsets: , duration: )
複製程式碼
let image = UIImage.animatedImageNamed("voice", duration: 2)
self.imageView.image = image
複製程式碼

其中voice1-3 已經命名好,放在Assets.xcassets

##CoreAnimation核心動畫 Core Animation可以用在 Mac OS X 和 iOS平臺. Core Animation的動畫執行過程是在後臺操作的.不會阻塞主執行緒. 要注意的是, Core Animation是直接作用在CALayer上的.並非UIView

  • CABasicAnimation 基礎動畫
  • CAKeyframeAnimation 關鍵幀動畫
  • CATransition 轉場動畫
  • CAAnimationGroup 組動畫
  • CASpringAnimation 彈性動畫 (iOS9.0之後新增CASpringAnimation類,它實現彈簧效果的動畫,是CABasicAnimation的子類。)

動畫操作過程:

  • 建立一個CAAnimation物件
  • 設定一些動畫的相關屬性
  • 給CALayer新增動畫(addAnimation:forKey: 方法)
  • 移除CALayer中得動畫(removeAnimationForKey: 方法)

###CAAnimation (一部分屬性來自 CAMediaTiming)

enter image description here

  1. duration:動畫的持續時間,預設為0.25秒

  2. speed :速度 speed = 1.0 / duration = 1.0 的動畫效果 和 speed = 2.0 / duration = 2.0 的動畫效果是一模一樣的,我們設定的duration可能和動畫進行的真實duration不一樣,這個還依賴於speed。

  3. timeOffset 設定動畫線的起始結束時間點

//假定一個3s的動畫,它的狀態為t0,t1,t2,t3,當沒有timeOffset的時候,正常的狀態序列應該為:
//t0->t1->t2->t3
//當設定timeOffset為1的時候狀態序列就變為
//t1->t2->t3->t0
//同理當timeOffset為2的時候狀態序列就變為:
//t2->t3->t0->t1
複製程式碼
  1. autoreverses:是否自動回到動畫開始狀態

  2. repeatCount:動畫的重複次數

  3. repeatDuration:動畫的重複時間

  4. removedOnCompletion:預設為YES,代表動畫執行完畢後就從圖層上移除,圖形會恢復到動畫執行前的狀態。如果想讓圖層保持顯示動畫執行後的狀態,那就設定為NO,不過還要設定fillMode屬性為kCAFillModeForwards

  5. fillMode:決定當前物件在非active時間段的行為.比如動畫開始之前,動畫結束之後

  6. beginTime:可以用來設定動畫延遲執行時間,若想延遲2s,就設定為CACurrentMediaTime()+2,CACurrentMediaTime()為圖層的當前時間。 CALayer 的beginTime 一般用於動畫暫停的使用,CAAnimation 的beginTime一般用於動畫延遲執行,但只在使用groupAnimation的時候生效,直接新增在layer上的animation使用會導致動畫不執行。

  7. timingFunction:速度控制函式,控制動畫執行的節奏

列舉引數:

kCAMediaTimingFunctionLinear  時間曲線函式,勻速
kCAMediaTimingFunctionEaseIn  時間曲線函式,由慢到特別快
kCAMediaTimingFunctionEaseOut  時間曲線函式,由快到慢
kCAMediaTimingFunctionEaseInEaseOut  時間曲線函式,由慢到快
kCAMediaTimingFunctionDefault   系統預設
複製程式碼
  1. delegate:動畫代理,一般設定隱式代理,該代理是NSObject的分類,需要遵守協議CAAnimationDelegate
-(void)animationDidStart:(CAAnimation *)anim; 核心動畫開始時執行

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; 核心動畫執行結束後呼叫
複製程式碼

CAPropertyAnimation

屬性:

keyPath:通過指定CALayer的一個屬性名做為keyPath裡的引數(NSString型別),並且對CALayer的這個屬性的值進行修改,達到相應的動畫效果。比如,指定@”position”為keyPath,就修改CALayer的position屬性的值,以達到平移的動畫效果。

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
複製程式碼

一些常用的animationWithKeyPath值的總結

說明 使用形式
transform.scale 比例轉化 @(0.8)
transform.scale.x 寬的比例 @(0.8)
transform.scale.y 高的比例 @(0.8)
transform.rotation.x 圍繞x軸旋轉 @(M_PI)
transform.rotation.y 圍繞y軸旋轉 @(M_PI)
transform.rotation.z 圍繞z軸旋轉 @(M_PI)
cornerRadius 圓角的設定 @(50)
backgroundColor 背景顏色的變化 (id)[UIColor purpleColor].CGColor
bounds 大小,中心不變 [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)];
position 位置(中心點的改變) [NSValue valueWithCGPoint:CGPointMake(300, 300)];
contents 內容,比如UIImageView的圖片 imageAnima.toValue = (id)[UIImage
opacity 透明度 @(0.7)
contentsRect.size.width 橫向拉伸縮放 @(0.4)最好是0~1之間的

####CABasicAnimation基本動畫 CABasicAnimation能實現諸多的動畫,移動,旋轉,縮放....KeyPath所涉及的都能實現

1.fromValue : keyPath相應屬性的初始值 **2.toValue **: keyPath相應屬性的結束值,到某個固定的值(類似transform的make含義) 注意:隨著動畫的進行,在長度為duration的持續時間內,keyPath相應屬性的值從fromValue漸漸地變為toValue. 如果fillMode = kCAFillModeForwards和removedOnComletion = NO;那麼在動畫執行完畢後,圖層會保持顯示動畫執行後的狀態,但實質上,圖層的屬性值還是動畫執行前的初始值,並沒有真正被改變.比如: CALayer的postion初始值為(0,0),CABasicAnimation的fromValue為(10,10),toValue為 (100,100),雖然動畫執行完畢後圖層保持在(100,100) 這個位置,實質上圖層的position還是為(0,0); 3.byValue:不斷進行累加的數值(byvalue 值加上fromValue => tovalue)

Position

let baseAnimation = CABasicAnimation(keyPath: "position.x")
baseAnimation.fromValue = objectView.center.x
baseAnimation.toValue = objectView.center.x + 100
baseAnimation.duration = 1
//逆行動畫
baseAnimation.autoreverses = true
baseAnimation.repeatCount = MAXFLOAT

//防止動畫接收後回到初始狀態
baseAnimation.isRemovedOnCompletion = false
baseAnimation.fillMode = CAMediaTimingFillMode.forwards

objectView.layer.add(baseAnimation, forKey: "demo")
複製程式碼

Scale

let baseAnimation = CABasicAnimation(keyPath: "transform.scale")
baseAnimation.fromValue = 0.5
baseAnimation.toValue = 1
baseAnimation.duration = 1
baseAnimation.repeatCount = MAXFLOAT
baseAnimation.fillMode = CAMediaTimingFillMode.forwards
objectView.layer.add(baseAnimation, forKey: "demo")
複製程式碼

####CASpringAnimation彈性動畫 iOS9才引入的動畫類,它繼承於CABaseAnimation,用於製作彈簧動畫

引數說明:

mass: 質量,影響圖層運動時的彈簧慣性,質量越大,彈簧拉伸和壓縮的幅度越大

stiffness: 剛度係數(勁度係數/彈性係數),剛度係數越大,形變產生的力就越大,運動越快

damping: 阻尼係數,阻止彈簧伸縮的係數,阻尼係數越大,停止越快

initialVelocity: 初始速率,動畫檢視的初始速度大小 速率為正數時,速度方向與運動方向一致,速率為負數時,速度方向與運動方向相反

settlingDuration: 結算時間 返回彈簧動畫到停止時的估算時間,根據當前的動畫引數估算 通常彈簧動畫的時間使用結算時間比較準確

let springAnimation = CASpringAnimation(keyPath: "position.x")
springAnimation.damping = 5
springAnimation.stiffness = 100;
springAnimation.mass = 1;
springAnimation.initialVelocity = 0;
springAnimation.fromValue = objectView.layer.position.x;
springAnimation.toValue = objectView.layer.position.x + 50;
springAnimation.duration = springAnimation.settlingDuration;
objectView.layer.add(springAnimation, forKey: springAnimation.keyPath);
複製程式碼

####CAKeyframeAnimation關鍵幀動畫

  • CAKeyframeAnimation跟CABasicAnimation的區別是: CABasicAnimation只能從一個數值(fromValue)變到另一個數值(toValue),而CAKeyframeAnimation會使用一個NSArray儲存這些數值.
  • CAKeyframeAnimation屬性解析: values:就是上述的NSArray物件。裡面的元素稱為”關鍵幀”(keyframe)。動畫物件會在指定的時間(duration)內,依次顯示values陣列中的每一個關鍵幀 . path:如果你設定了path,那麼values將被忽略. keyTimes:可以為對應的關鍵幀指定對應的時間點,其取值範圍為0到1.0,keyTimes中的每一個時間值都對應values中的每一幀.當keyTimes沒有設定的時候,各個關鍵幀的時間是平分的. 注: CABasicAnimation能實現的CAKeyframeAnimation也能實現,而且更具體和準確

Shake Sample

let shakeAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
//設定晃動角度
let angle = Double.pi / 2
//設定關鍵幀動畫的值
shakeAnimation.values = [angle, -angle, angle]
//設定關鍵幀動畫每幀的執行時間,這裡不設定也行,預設平均分配時間
shakeAnimation.keyTimes = [0, 0.5, 1]
//設定動畫重複次數,預設為1次
shakeAnimation.repeatCount = MAXFLOAT
//設定動畫執行效果
shakeAnimation.timingFunctions = [CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)]
//設定相鄰動畫過渡方式
shakeAnimation.calculationMode = CAAnimationCalculationMode.cubic
objectView.layer.add(shakeAnimation, forKey: shakeAnimation.keyPath);
複製程式碼

軌跡動畫

let path = UIBezierPath()
//設定動畫的執行路徑為一個M的形狀
path.move(to: CGPoint(x: 40, y: 300))
path.addLine(to: CGPoint(x: 80, y: 150))
path.addLine(to: CGPoint(x: 120, y: 300))
path.addLine(to: CGPoint(x: 160, y: 150))
path.addLine(to: CGPoint(x: 200, y: 300))
let bezierAnimation = CAKeyframeAnimation(keyPath: "position")
//由於CAKeyframeAnimation的path為CGPath,所以這裡要轉換一次
bezierAnimation.path = path.cgPath
//設定動畫時間
bezierAnimation.duration = 4
//自動旋轉layer角度與path相切
bezierAnimation.rotationMode = CAAnimationRotationMode.rotateAuto
//設定動畫重複次數
bezierAnimation.repeatCount = MAXFLOAT
//設定自動逆向
bezierAnimation.autoreverses = true
objectView.layer.add(bezierAnimation, forKey: nil)
複製程式碼

Scale動畫

let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
scaleAnimation.values = [0.0, 0.4, 0.8, 1.2, 1.6, 1.2, 0.8, 0.4, 0.0]
scaleAnimation.duration = 2
scaleAnimation.autoreverses = true
scaleAnimation.repeatCount = MAXFLOAT
objectView.layer.add(scaleAnimation, forKey: nil)
複製程式碼

###CAAnimationGroup組合動畫 將多個動畫組合和併發執行 delegate 和 isRemovedOnCompletion 在動畫的屬性陣列中目前被忽略。 CAAnimationGroup 的 delegate 接收這些訊息

  • animations CAAnimation 陣列,用於新增多個 CAAnimation 動畫
let animationPath = CAKeyframeAnimation.init(keyPath: "position")
animationPath.path = path.cgPath
animationPath.rotationMode = CAAnimationRotationMode.rotateAuto

//旋轉
let rotate:CABasicAnimation = CABasicAnimation()
rotate.keyPath = "transform.rotation"
rotate.toValue = Double.pi

//縮小圖片到0
let scale:CABasicAnimation = CABasicAnimation()
scale.keyPath = "transform.scale"
scale.toValue = 0.0

//組合動畫
let animationGroup:CAAnimationGroup = CAAnimationGroup()
animationGroup.animations = [animationPath,rotate,scale];
animationGroup.duration = 2.0;
animationGroup.fillMode = CAMediaTimingFillMode.forwards;
animationGroup.isRemovedOnCompletion = false
objectView.layer.add(animationGroup, forKey:
    nil)
複製程式碼

###CATransition轉場動畫

CAAnimation的子類 在圖層狀態之間提供動畫轉換的物件 提供了一個圖層之間的過渡的動畫

CATransition 有一個 type 和 subtype 來標識變換效果

新增加的屬性

  • startProgress 開始的進度 0~1
  • endProgress 結束時的進度 0~1
  • type 轉換型別
    • kCATransitionFade (default)
    • kCATransitionMoveIn
    • kCATransitionPush
    • kCATransitionReveal
  • API引入的type,在蘋果官網是不會承認的,所以不建議使用
    • 1 animation.type = @"cube"; //立方體效果
    • 2 animation.type = @"suckEffect";//猶如一塊布被抽走
    • 3 animation.type = @"oglFlip"; //上下翻轉效果
    • 4 animation.type = @"rippleEffect"; //滴水效果
    • 5 animation.type = @"pageCurl"; //向左翻頁
    • 6 animation.type = @"pageUnCurl"; //向下翻頁
  • subtype 基於運動方向預定義的轉換
    • kCATransitionFromLeft
    • kCATransitionFromRight
    • kCATransitionFromTop
    • kCATransitionFromBottom
  • filter 濾鏡

** View Transaition**

let animation = CATransition()
animation.duration = 1.0
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
// `fade', `moveIn', `push' and `reveal'. Defaults to `fade'
animation.type = CATransitionType.reveal
// `fromLeft', `fromRight', `fromTop' and `fromBottom'
animation.subtype = CATransitionSubtype.fromLeft
//        animation.isRemovedOnCompletion = true
animation.startProgress = 0.5
objectView.layer.add(animation, forKey: nil)
複製程式碼

** ViewController Transaition(翻頁效果)**

let vc = UIStoryboard(name: "Main", bundle: nil)
    .instantiateViewController(withIdentifier: "second")

let anima = CATransition.init()
//        anima.type = CATransitionType.reveal
anima.type = CATransitionType(rawValue: "pageUnCurl")
anima.subtype = CATransitionSubtype.fromLeft
anima.duration = 1.0

UIApplication.shared.keyWindow?.layer.add(anima, forKey: "pageUnCurl")
//        UIApplication.shared.keyWindow?.layer.removeAnimation(forKey: "pageUnCurl")
self.navigationController?.pushViewController(vc, animated: false)
複製程式碼

##其他動畫

###控制器轉場動畫 UIViewControllerAnimatedTransitioning

####一、 轉場動畫-modal 給vc的transitioningDelegate屬性賦值,為即將跳轉的vc指定轉場動畫代理,協議中有兩個基礎方法,分別要求代理返回present時的動畫以及dismiss時的動畫。

class FadeTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate  {
    private lazy var fadeAnimator = FadeAnimator()
    
    // 提供dismiss的時候使用到的動畫執行物件
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        fadeAnimator.isPresenting = false
        return fadeAnimator
    }
    
    // 提供present的時候使用到的動畫執行物件
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        fadeAnimator.isPresenting = true
        return fadeAnimator
    }
}
複製程式碼

寫一個類專門來實現UIViewControllerAnimatedTransitioning動畫協議,作為動畫的實現類。然後代理就可以返回兩個動畫實現物件,實現UIViewControllerTransitioningDelegate轉場代理協議。

獲取轉場過程的三個檢視:containerView、fromView、toView。 containerView是動畫過程中提供的暫時容器。 fromView是轉場開始頁的檢視。 toView是轉場結束頁的檢視。

轉場的過程,大多數情況下我們都是對toView作各種變換操作,例如改變toView的alpha,size,旋轉等等。 在對它進行操作前,需要先把它放到container上才能顯示出來。[container addSubview:toView];

class FadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    
    let duration = 1.0
    var isPresenting = true
    
    // 指定轉場動畫持續的時間
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return duration
    }
    
    // 實現轉場動畫的具體內容
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
        // 得到容器檢視
        let containerView = transitionContext.containerView
        // 目標檢視
        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
        
        containerView.addSubview(toView)
        
        // 為目標檢視的展現新增動畫
        toView.alpha = 0.0
        UIView.animate(withDuration: duration,
                       animations: {
                        toView.alpha = 1.0
        }, completion: { _ in
            transitionContext.completeTransition(true)
        })
    }
}
複製程式碼

最後presentViewController的時候賦值代理

let transitionDelegate = FadeTransitionDelegate()
 @IBAction func UIViewControllerAnimatedTransitioning_Demo(_ sender: Any) {
     let vc = UIStoryboard(name: "Main", bundle: nil)
         .instantiateViewController(withIdentifier: "second")
     
     vc.transitioningDelegate = transitionDelegate
     present(vc, animated: true, completion: nil)
 }
複製程式碼

####二、 轉場動畫-push 流程同 轉場動畫基礎用法-modal 要自定義push動畫,需實現導航控制器的代理協議 UINavigationControllerDelegate

class PushTansitionDelegate: NSObject, UINavigationControllerDelegate {
    private lazy var fadeAnimator = FadeAnimator()
    // 是否需要互動
    var interactive = false
    
    //返回一個不可互動的轉場動畫
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return fadeAnimator
    }
}
複製程式碼

其中fadeAnimator和Model裡的是同樣的定義。 使用Push動畫

let vc = UIStoryboard(name: "Main", bundle: nil)
    .instantiateViewController(withIdentifier: "second")

self.navigationController?.delegate = pushTransitionDelegate
self.navigationController?.pushViewController(vc, animated: true)
複製程式碼

####一、 轉場動畫-互動式轉場動畫 互動式過渡是由事件驅動的。可以是動作事件或者手勢,通常為手勢。要實現一個互動式過渡,除了需要跟之前相同的動畫,還需要告訴互動控制器動畫完成了多少。開發者只需要確定已經完成的百分比,其他交給系統去做就可以了。例如,(平移和縮放的距離 / 速度的量可以作為計算完成的百分比的引數)。

互動式控制器實現了 UIViewControllerInteractiveTransitioning 協議:

- (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
複製程式碼

這個方法裡只能有一個動畫塊,動畫應該基於 UIView 而不是圖層,互動式過渡不支援 CATransition 或 CALayer 動畫。

互動式過渡的互動控制器應當是 UIPercentDrivenInteractiveTransition 子類。動畫類負責計算完成百分比,系統會自動更新動畫的中間狀態。

- (void)updateInteractiveTransition:(CGFloat)percentComplete;
- (void)cancelInteractiveTransition;
- (void)finishInteractiveTransition;
複製程式碼

對FadeAnimator新增handlePan,並且繼承UIPercentDrivenInteractiveTransition 支援互動式動畫

class FadeAnimator: UIPercentDrivenInteractiveTransition, UIViewControllerAnimatedTransitioning {
    
    let durationAnimation = 1.0
    
    //  present/dismiss, push/pop
    var isPresenting = true
    // 是否需要互動
    var interactive = false
    
    // 指定轉場動畫持續的時間
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return durationAnimation
    }
    
    // 實現轉場動畫的具體內容
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
        // 得到容器檢視
        let containerView = transitionContext.containerView
        // 目標檢視
        let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
        
        containerView.addSubview(toView)
        
        // 為目標檢視的展現新增動畫
        toView.alpha = 0.0
        UIView.animate(withDuration: durationAnimation,
                       animations: {
                        toView.alpha = 1.0
        }, completion: { _ in
            transitionContext.completeTransition(true)
        })
    }
    
    func handlePan(recognizer: UIPanGestureRecognizer) {
        let translation = recognizer.translation(in: recognizer.view!.superview!)
        var progress: CGFloat = abs(translation.x / 200.0)
        progress = min(max(progress, 0.01), 0.99)
        
        switch recognizer.state {
        case .changed:
            // 更新當前轉場動畫播放進度
            update(progress)
        case .cancelled:
            cancel()
        case .ended:
            finish()
        default:
            break
        }
    }
複製程式碼

對應的Delegate更改為 支援互動的轉場動畫:

class PushTansitionDelegate: NSObject, UINavigationControllerDelegate {
    private lazy var fadeAnimator = FadeAnimator()
    // 是否需要互動
    var interactive = false
    
    //返回一個不可互動的轉場動畫
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return fadeAnimator
    }
    
    // 返回一個可以互動的轉場動畫
    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        if interactive == false {
            return nil
        }
        
        return fadeAnimator
    }
    

    func handlePan(recognizer: UIPanGestureRecognizer) {
        fadeAnimator.handlePan(recognizer: recognizer)
    }
}
複製程式碼

控制器中使用,第二個頁面返回的時候使用互動式轉場動畫

class SecondViewController: UIViewController {
     let pushTransitionDelegate = PushTansitionDelegate()
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(recognizer:)))
        self.view.addGestureRecognizer(pan)
    }
    
    @objc func handlePan(recognizer: UIPanGestureRecognizer) {
        if recognizer.state == .began {
            
            pushTransitionDelegate.interactive = true
            self.navigationController?.delegate = pushTransitionDelegate
            self.navigationController?.popViewController(animated: true)
        }else {
            pushTransitionDelegate.handlePan(recognizer: recognizer)
        }
    }
    
    @IBAction func close(_ sender: Any) {
        self.dismiss(animated: true, completion: nil)
    }
}
複製程式碼

###UIDynamic-動力學框架 UIDynamic是蘋果在iOS7之後新增的一套動力學框架,運用它我們可以極其方便地模擬現實生活中的運動,比如重力,碰撞等等。它是通過新增行為的方式讓動力學元素參與運動的。

iOS7.0中提供的動力學行為包括:

UIGravityBehavior:重力行為 UICollisionBehavior:碰撞行為 UIAttachmentBehavior:附著行為 UISnapBehavior:吸附行為 UIPushBehavior:推行為 UIDynamicItemBehavior:動力學元素行為

UIDynamic的使用還是相對簡單

1.首先我們建立一個小方塊 objectView 並把它放在self.view的上面部分。(只有遵循了UIDynamicItem協議的物件才能參與模擬模擬,而UIView正遵循了此協議,因此所有檢視控制元件都能參與模擬運動)

2.然後定義一個 UIDynamicAnimator 物理模擬器(凡是要參與運動的物件必須新增到此容器中)

3.再新增一個重力行為 到模擬器,並且 這個行為作用物件是我們之前定義的boxView

4.可以發現 放在self.view上半部分的boxView受重力行為影響,往下掉落。但是會掉出self.view範圍。

5.為了不掉出self.view 範圍 我們還需要給objectView新增一個別的行為:碰撞行為,接觸到模擬器邊界或者其他self.view中得容器會產生碰撞效果。

6.這樣小方塊就不會掉出模擬器範圍了,同理,其他行為的使用方式和上面一樣,一定要新增到模擬器才能生效。

var animator: UIDynamicAnimator!
@IBAction func UIDynamic_tap(_ sender: Any) {
    animator = UIDynamicAnimator(referenceView: self.view)
    let behavior = UIGravityBehavior(items: [objectView])
    animator.addBehavior(behavior)
    
    let behaviorCollision = UICollisionBehavior(items: [objectView])
    behaviorCollision.translatesReferenceBoundsIntoBoundary = true
    animator.addBehavior(behaviorCollision)
}
複製程式碼

CAEmitterLayer 粒子動畫

1、CAEmitterLayer。 這個主要是定義粒子原型發射層的形狀和發射位置,發射源的尺寸以及發射的模式等。

2、CAEmitterCell 單個粒子的原型,通常有多個,根據cell的屬性和CAEmitterCell的配置,由uikit隨機生成,粒子原型的屬性包括粒子的圖片,顏色,方向,運動,縮放比例和生命週期等。

這兩個類的引數看起來似乎很簡單,但這些引數的不同組合配合上相對應圖片,則可以實現許多意想不到的動畫效果。

var rainLayer: CAEmitterLayer!
@IBAction func CAEmitterLayer_tap(_ sender: Any) {
    // 粒子發射圖層
    rainLayer = CAEmitterLayer()
    // 發射器形狀為線形,預設發射方向向上
    rainLayer.emitterShape = CAEmitterLayerEmitterShape.line
    // 從發射器的輪廓發射粒子
    rainLayer.emitterMode = CAEmitterLayerEmitterMode.outline
    // 優先渲染舊的粒子
    rainLayer.renderMode = CAEmitterLayerRenderMode.oldestFirst
    // 發射位置
    // 對於線形發射器,線的兩端點分別為
    // (emitterPosition.x - emitterSize.width/2, emitterPosition.y, emitterZPosition)和
    // (emitterPosition.x + emitterSize.width/2, emitterPosition.y, emitterZPosition)
    rainLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: 0)
    // 發射器大小
    rainLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)
    // 粒子生成速率的倍數,一開始不發射,設定為零
    rainLayer.birthRate = 0
    
    // 發射的粒子
    let cell = CAEmitterCell()
    // 粒子顯示的內容,設定CGImage,顯示圖片
    cell.contents = UIImage(named: "star")?.cgImage
    // 粒子縮放倍數
    cell.scale = 0.1
    // 粒子壽命,單位是秒
    cell.lifetime = 5
    // 粒子生成速率,單位是個/秒,實際顯示效果要乘以CAEmitterLayer的birthRate
    cell.birthRate = 1000
    // 粒子速度
    cell.velocity = 500
    // 粒子發射角度,正值表示順時針方向
    cell.emissionLongitude = CGFloat.pi
    
    // 圖層要發射1種粒子
    rainLayer.emitterCells = [cell]
    // 新增粒子發射圖層
    view.layer.addSublayer(rainLayer)
    
    
    
    //  粒子生成速率漸變動畫
    let birthRateAnimation = CABasicAnimation(keyPath: "birthRate")
    birthRateAnimation.duration = 3
    if rainLayer.birthRate == 0 {
        // 雨變大
        birthRateAnimation.fromValue = 0
        birthRateAnimation.toValue = 1
        rainLayer.birthRate = 1
    } else {
        // 雨變小
        birthRateAnimation.fromValue = 1
        birthRateAnimation.toValue = 0
        rainLayer.birthRate = 0
    }
    // 加入動畫
    rainLayer.add(birthRateAnimation, forKey: "birthRate")
}
複製程式碼

參考連線 www.jianshu.com/p/71f2fa270… www.jianshu.com/p/9aead7675… www.jianshu.com/p/802d47f0f…

相關文章