動畫 - UIKit
動畫原理
- 視覺殘留效應
- 運動模糊
做動畫的時候要達到 60FPS 時候,畫面才能流暢辟癌,不然用戶會感覺界面卡頓。
UIView 提供的動畫支持
UIView 動畫本質上對 Core Animation 的封裝荐捻,提供一個簡潔好用的動畫接口黍少,在要求不復雜的情況下寡夹,完全可以實現(xiàn)很多動畫。
UIView 動畫可以設置的動畫屬性有:
- frame / bounds 大小變化
- center 中心位置
- transform 旋轉平移等
- alpha 透明度
- backgroundColor 背景顏色
- contentStretch 拉伸內(nèi)容
- Autolayout環(huán)境下的動畫要直接修改constraint,注意 setNeedsUpdateConstraints,layoutIfNeeded的用法
UIView 類方法動畫
- 動畫的開始和結束方法
- UIView Block動畫
- Spring 動畫
- Keyframes 動畫
- 轉場動畫
- 單個視圖的過渡
- 從舊視圖到新視圖的過渡
1. 動畫的開始和結束方法
基本語句:
動畫開始結束標記
UIView.beginAnimations(String?, context: UnsafeMutablePointer<Void>)
第一個參數(shù)是動畫標識厂置,第二個參數(shù)是附加參數(shù)菩掏,在設置了代理的情況下,此參數(shù)將發(fā)送setAnimationWillStartSelector
和 setAnimationDidStopSelector
所指定的方法昵济,一般設為 nil智绸。
UIView.commitAnimations()
動畫結束
動畫參數(shù)設置方法
-
UIView.setAnimationDelay(NSTimeInterval)
設置動畫的延時 -
UIView.setAnimationDuration(NSTimeInterval)
設置動畫持續(xù)時間 -
UIView.setAnimationDelegate(AnyObject?)
設置動畫代理 -
UIView.setAnimationWillStartSelector(Selector)
設置動畫即將開始時代理執(zhí)行的SEL -
UIView.setAnimationDidStopSelector(Selector)
設置動畫結束時代理對象執(zhí)行的SEL -
UIView.setAnimationRepeatCount(Float)
設置動畫的重復次數(shù) -
UIView.setAnimationCurve(UIViewAnimationCurve)
設置動畫的曲線 -
UIView.setAnimationRepeatAutoreverses(Bool)
設置動畫是否繼續(xù)執(zhí)行相反的動畫 -
UIView.setAnimationsEnabled(Bool)
是否禁用動畫效果(對象屬性依然會被改變,只是沒有動畫效果) -
UIView.setAnimationBeginsFromCurrentState(Bool)
設置是否從當前狀態(tài)開始播放動畫- 假設上一個動畫正在播放砸紊,且尚未播放完畢,我們將要進行一個新的動畫:
- 當為true時:動畫將從上一個動畫所在的狀態(tài)開始播放
- 當為false時:動畫將從上一個動畫所指定的最終狀態(tài)開始播放(此時上一個動畫馬上結束)
以下是簡單的例子:
2. UIView Block 動畫
1)最簡單的 Block 動畫 包含時間和動畫
UIView.animateWithDuration(NSTimeInterval) { // 動畫持續(xù)時間
<#code#>//執(zhí)行動畫
}
2)帶有動畫完成回調(diào)的 Block 動畫
UIView.animateWithDuration(NSTimeInterval, animations: {
<#code#>//執(zhí)行動畫
}) { (Bool) in
<#code#>// 動畫完畢后執(zhí)行的操作
}
3)可以設置延遲和過渡效果 Block 動畫
UIView.animateWithDuration(NSTimeInterval, delay: NSTimeInterval, options: UIViewAnimationOptions, animations: {
<#code#>
}) { (<#Bool#>) in
<#code#>
}
注意囱挑,此處的 UIViewAnimationOptions 可以組合使用,在 swift 中寫法 options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.Repeat]
具體的枚舉值醉顽,看官方文檔即可。
4)Spring 動畫
iOS7 后新增 Spring 動畫平挑,iOS 系統(tǒng)動畫大部分采用 Spring Animation游添。
UIView.animateWithDuration(NSTimeInterval, delay: NSTimeInterval, usingSpringWithDamping: CGFloat, initialSpringVelocity: CGFloat, options: UIViewAnimationOptions, animations: {
<#code#>
}) { (<#Bool#>) in
<#code#>
}
- Duration: 動畫持續(xù)時間
- delay: 動畫執(zhí)行延時
- usingSpringWithDamping: 震動效果,范圍 0~1通熄,數(shù)值越小唆涝,震動效果越明顯
- initialSpringVelocity: 初始速度
- options: 動畫的過渡效果
5)Keyframes 關鍵幀動畫
UIView.animateKeyframesWithDuration(NSTimeInterval, delay: NSTimeInterval, options: UIViewKeyframeAnimationOptions, animations: {
<#code#>
}) { (<#Bool#>) in
<#code#>
}
增加關鍵幀的方法
UIView.addKeyframeWithRelativeStartTime(Double, relativeDuration: Double, animations: {
<#code#>
})
注意開始時間和持續(xù)時間均是占總時間的比例
UIViewKeyframeAnimationOptions 的枚舉值如下,可以組合使用
UIViewAnimationOptionLayoutSubviews //進行動畫時布局子控件
UIViewAnimationOptionAllowUserInteraction //進行動畫時允許用戶交互
UIViewAnimationOptionBeginFromCurrentState //從當前狀態(tài)開始動畫
UIViewAnimationOptionRepeat //無限重復執(zhí)行動畫
UIViewAnimationOptionAutoreverse //執(zhí)行動畫回路
UIViewAnimationOptionOverrideInheritedDuration //忽略嵌套動畫的執(zhí)行時間設置
UIViewAnimationOptionOverrideInheritedOptions //不繼承父動畫設置
UIViewKeyframeAnimationOptionCalculationModeLinear //運算模式 :連續(xù)
UIViewKeyframeAnimationOptionCalculationModeDiscrete //運算模式 :離散
UIViewKeyframeAnimationOptionCalculationModePaced //運算模式 :均勻執(zhí)行
UIViewKeyframeAnimationOptionCalculationModeCubic //運算模式 :平滑
UIViewKeyframeAnimationOptionCalculationModeCubicPaced //運算模式 :平滑均勻
關鍵幀動畫:
private func blockAni5() {
UIView.animateKeyframesWithDuration(5, delay: 0.0, options: UIViewKeyframeAnimationOptions.CalculationModeLinear, animations: {
UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 1.0/4, animations: {
self.greenView.backgroundColor = UIColor.redColor()
})
UIView.addKeyframeWithRelativeStartTime(1.0/4, relativeDuration: 1.0/4, animations: {
self.greenView.backgroundColor = UIColor.blackColor()
self.greenView.frame.size = CGSize(width: 50, height: 50)
})
UIView.addKeyframeWithRelativeStartTime(2.0/4, relativeDuration: 1.0/4, animations: {
self.greenView.backgroundColor = UIColor.yellowColor()
})
UIView.addKeyframeWithRelativeStartTime(3.0/4, relativeDuration: 1.0/4, animations: {
self.greenView.backgroundColor = UIColor.blueColor()
self.greenView.frame.size = CGSize(width: 250, height: 250)
})
}) { (_) in
print("動畫完成blockAni5()")
}
}
簡單的例子:
3. UIView 轉場動畫
在進行示例之前唇辨,大家需要注意一點過渡轉變動畫與動畫屬性動畫的不同之處廊酣。我們在創(chuàng)建動畫屬性動畫時只需要在animations閉包中添加對視圖動畫屬性修改的代碼即可,它沒有作用域或作用視圖的概念赏枚。而在過渡轉變動畫中有作用視圖的概念亡驰,也就是說我們調(diào)用過渡轉變動畫方法時需要指定一個作用視圖
過渡轉變動畫中的作用視圖并不是我們的目標視圖,而是目標視圖的容器視圖饿幅,那么大家不難想象凡辱,如果該容器視圖中有多個子視圖,那么這些子視圖都會有過渡轉變動畫效果栗恩。
1)從舊視圖到新視圖的轉場
UIView.transitionFromView(UIView, toView: UIView, duration: NSTimeInterval, options: UIViewAnimationOptions) { (<#Bool#>) in
<#code#>
}
在該動畫過程中透乾,fromView 會從父視圖中移除,并將 toView 添加到父視圖中磕秤。轉場動畫的作用對象是父視圖乳乌,過渡效果體現(xiàn)在父視圖上
2)單個試圖的過渡
UIView.transitionWithView(UIView, duration: NSTimeInterval, options: UIViewAnimationOptions, animations: {
<#code#>
}) { (<#Bool#>) in
<#code#>
}
簡單的例子
核心動畫 CoreAnimations
Core Animation(核心動畫)是一組強大的動畫 API,是直接操作 CALayer 層來產(chǎn)生動畫市咆,相比上述的 UIView 動畫钦扭,可以實現(xiàn)更復雜的動畫效果。
事務管理 CATransaction
CALayer 的可用于動畫的屬性成為 Animatable properties床绪,蘋果官方有詳細的列表客情,顯示了所有了可以動畫的屬性 CALayer Animatable Properties其弊。如果一個Layer對象對應著 View,則稱這個 Layer 是一個 Root Layer, 非 Root Layer 一般是通過 CALayer 或者其子類直接創(chuàng)建的膀斋。
所有的非 Root Layer 在設置 Amimation Properties 的時候都存在隱式動畫梭伐,默認的 duration 是0.25秒
事務(transaction)實際上是Core Animation用來包含一系列屬性動畫集合的機制,用指定事務去改變可以做動畫的圖層屬性仰担,不會立刻發(fā)生變化糊识,而是提交事務時用一個動畫過渡到新值。任何 Layer 的可動畫屬性的設置都屬于某個 CATransaction摔蓝,事務的作用是為了保證多個屬性的變化同時進行赂苗。事務可以嵌套,當事務嵌套時候贮尉,只有最外層的事務 commit 之后拌滋,整個動畫才開始。
CATransaction沒有任何實例方法猜谚,只有類型方法败砂。CATransaction.begin()
和CATransaction.commit()
構成了一個動畫塊:
CATransaction.begin()
/* animation block */
CATransaction.commit()
其他的方法
func animationDuration() -> CFTimeInterval // get duration, defaults to 0.25s
func setAnimationDuration(dur: CFTimeInterval) // set duration
func animationTimingFunction() -> CAMediaTimingFunction? // get timing function
func setAnimationTimingFunction(function: CAMediaTimingFunction?) // set timing function
func disableActions() -> Bool // get disable actions state
func setDisableActions(flag: Bool) // set disable actions state
func completionBlock() -> (() -> Void)? // get completion block
func setCompletionBlock(block: (() -> Void)?) // set completion block
以上四組的方法可以用以下兩個方法代替
func valueForKey(key: String) -> AnyObject?
func setValue(anObject: AnyObject?, forKey key: String)
CATransaction
動畫塊只能處理CALayer相關動畫,無法正確處理UIView的動畫魏铅,甚至UIView的 Root layer(與UIView相關聯(lián)的CALayer)也不行昌犹。
UIView 的 Root layer動畫為什么會在CATransaction動畫塊中失效?
隱式動畫的查找過程如下:
禁止隱式動畫:
我們把改變屬性時CALayer自動應用的動畫稱作行為览芳,當CALayer的屬性被修改時候斜姥,它會調(diào)用-actionForKey:方法,傳遞屬性的名稱沧竟。剩下的操作都在CALayer的頭文件中有詳細的說明疾渴,實質上是如下幾步:
- 圖層首先檢測它是否有委托,并且是否實現(xiàn)CALayerDelegate協(xié)議指定的-actionForLayer:forKey方法屯仗。如果有搞坝,直接調(diào)用并返回結果。
- 如果沒有委托魁袜,或者委托沒有實現(xiàn)-actionForLayer:forKey方法桩撮,圖層接著檢查包含屬性名稱對應行為映射的actions字典。
如果actions字典沒有包含對應的屬性峰弹,那么圖層接著在它的style字典接著搜索屬性名店量。- 最后,如果在style里面也找不到對應的行為鞠呈,那么圖層將會直接調(diào)用定義了每個屬性的標準行為的-defaultActionForKey:方法融师。
所以一輪完整的搜索結束之后,-actionForKey:要么返回空(這種情況下將不會有動畫發(fā)生)蚁吝,要么是CAAction協(xié)議對應的對象旱爆,最后CALayer拿這個結果去對先前和當前的值做動畫舀射。
于是這就解釋了 UIKit 是如何禁用隱式動畫的:每個 UIView 對它關聯(lián)的圖層都扮演了一個委托,并且提供了
-actionForLayer:forKey
的實現(xiàn)方法怀伦。當不在一個動畫塊的實現(xiàn)中脆烟,UIView 對所有圖層行為返回 nil,但是在動畫 block 范圍之內(nèi)房待,它就返回了一個非空值邢羔。
- UIView關聯(lián)的圖層禁用了隱式動畫,對這種圖層做動畫的唯一辦法就是使用UIView的動畫函數(shù)(而不是依賴CATransaction)桑孩,或者繼承UIView拜鹤,并覆蓋-actionForLayer:forKey:方法,或者直接創(chuàng)建一個顯式動畫流椒。
- 對于單獨存在的圖層敏簿,我們可以通過實現(xiàn)圖層的-actionForLayer:forKey:委托方法,或者提供一個actions字典來控制隱式動畫镣隶。
參考資料 iOS Actions
時間系統(tǒng)
CAMediaTiming
協(xié)議定義了在一段動畫內(nèi)用來控制逝去時間的屬性的集合极谊。CALayer 通過CAMediaTiming
協(xié)議實現(xiàn)了一個有層級關系的時間系統(tǒng)诡右。
幾個重要屬性(都是CALayer的屬性):
- beginTime 是相對于父級對象的開始時間
- timeOffset是active local time的偏移量
- speed 設置當前對象的時間流逝相對于父級對象時間流的流逝速度
- fillMode 決定了當前對象過了非 active 時間段的行為
顯示動畫
當需要對非 Root Layer 進行動畫或者需要對動畫做更多的自定義的行為的時候安岂,需要使用顯示動畫,基類為 CAAnimation
核心動畫類中可以直接使用的類有:
- CABasicAnimation
- CAKeyframeAnimation
- CATransition
- CAAnimationGroup
- CASpringAnimation
CABasicAnimation有三個比較重要的屬性,fromValue,toValue,byValue,這三個屬性都是可選的,但不能同時多于兩個為非空.最終都是為了確定animation變化的起點和終點.中間的值都是通過插值方式計算出來的.插值計算的結果由timingFunction指定,默認timingFunction為nil,會使用liner的,也就是變化是均勻的.
1. 核心動畫類的核心方法
- 初始化CAAnimation對象
- 一般使用animation方法生成實例
let animation = CABasicAnimation()
- 如果是CAPropertyAnimation的子類帆吻,可以使用'let animation = CABasicAnimation(keyPath: String?)'來生成
- 一般使用animation方法生成實例
- 設置動畫的相關屬性
- 執(zhí)行時間
- 執(zhí)行曲線
- keyPath 的目標值
- 代理等
animation.duration = 2.0
// animation.fromValue = UIColor.blackColor()
animation.toValue = NSValue(CGPoint: CGPointMake(300, 300))
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
animation.removedOnCompletion = false
animation.fillMode = kCAFillModeForwards
- 動畫的添加和移除
- 調(diào)用 CALayer 的
view2.layer.addAnimation(animation, forKey: "color")
- 停止動畫
view2.layer.removeAnimationForKey(String)
和view2.layer.removeAllAnimations()
- 調(diào)用 CALayer 的
防止動畫結束后回到初始狀態(tài)
只需設置removedOnCompletion域那、fillMode兩個屬性就可以了。
transformAnima.removedOnCompletion = NO;
transformAnima.fillMode = kCAFillModeForwards;
解釋:為什么動畫結束后返回原狀態(tài)猜煮?
給一個視圖添加layer動畫時次员,真正移動并不是我們的視圖本身,而是 presentation layer 的一個緩存王带。動畫開始時 presentation layer開始移動淑蔚,原始layer隱藏,動畫結束時愕撰,presentation layer從屏幕上移除刹衫,原始layer顯示。這就解釋了為什么我們的視圖在動畫結束后又回到了原來的狀態(tài)搞挣,因為它根本就沒動過带迟。
這個同樣也可以解釋為什么在動畫移動過程中,我們?yōu)楹尾荒軐ζ溥M行任何操作囱桨。
所以在我們完成layer動畫之后仓犬,最好將我們的layer屬性設置為我們最終狀態(tài)的屬性,然后將presentation layer 移除掉舍肠。
2. 核心動畫類的常用屬性
- KeyPath:可以指定 KeyPath 為 CALayer 的屬性值搀继,并對它修改窘面,注意部分屬性是不支持動畫的
- duration:動畫的持續(xù)時間
- repeatCount: 動畫的重復次數(shù)
- timingFunction:動畫的時間節(jié)奏控制
- fillMode:視圖在非Active時的行為
- removedOnCompletion:動畫執(zhí)行完畢后是否從圖層上移除,默認為YES(視圖會恢復到動畫前的狀態(tài))律歼,可設置為NO(圖層保持動畫執(zhí)行后的狀態(tài)民镜,前提是fillMode設置為kCAFillModeForwards)
- beginTime:動畫延遲執(zhí)行時間(通過CACurrentMediaTime() + your time 設置)
- delegate:代理
func animationDidStart(anim: CAAnimation)
func animationDidStop(anim: CAAnimation, finished flag: Bool)
Timing Function對應的類是CAMediaTimingFunction,它提供了兩種獲得時間函數(shù)的方式,一種是使用預定義的五種時間函數(shù),一種是通過給點兩個控制點得到一個時間函數(shù). 相關的方法為
CAMediaTimingFunction(name: String)
CAMediaTimingFunction(controlPoints: Float, c1y: Float, c2x: Float, c2y: Float)
五種預定義的時間函數(shù)名字的常量變量分別為
- kCAMediaTimingFunctionLinear,
- kCAMediaTimingFunctionEaseIn,
- kCAMediaTimingFunctionEaseOut,
- kCAMediaTimingFunctionEaseInEaseOut,
- kCAMediaTimingFunctionDefault
自定義的 Timing Function 的函數(shù)圖像就是一條三次的貝塞爾曲線。
CAKeyframeAnimation動畫
兩個決定動畫關鍵幀的屬性:
- values: 關鍵幀數(shù)組對象险毁,里面每一個元素就是一個關鍵幀制圈,動畫會在相應時間段內(nèi),依次執(zhí)行數(shù)組中每一個關鍵幀動畫
- path: 動畫路徑對象畔况,可以指定一個路徑鲸鹦,在執(zhí)行動畫時會沿著路徑移動,path只能對CALayer的 anchorPoint 和 position 屬性起作用
- keyTimes: 設置關鍵幀對應的時間點跷跪。范圍0 ~ 1馋嗜,默認每一幀時間平分,keyTimes數(shù)組中的每個元素定義了相應的keyframe的持續(xù)時間值作為動畫的總持續(xù)時間的一小部分,每個元素的值必須大于吵瞻、或等于前一個值葛菇。
keyframeAni.keyTimes = [0.1,0.5,0.7,0.8,1]
- calculationMode 計算模式,其主要針對的是每一幀的內(nèi)容為一個座標點的情況,也就是對anchorPoint 和 position 進行的動畫橡羞,表示插值計算的模式
- kCAAnimationLinear 默認值 直線相連來差值
- kCAAnimationDiscrete 離散的眯停,不進行插值計算,所有關鍵幀逐個顯示
- kCAAnimationPaced 動畫均勻的卿泽,此時keytimes和timeFunctions無效
- kCAAnimationCubic 對關鍵幀為坐標點的關鍵幀進行圓滑曲線相連后插值計算,對于曲線的形狀還可以通過tensionValues,continuityValues,biasValues來進行調(diào)整自定義
- kCAAnimationCubicPaced 在kCAAnimationCubic的基礎上使得動畫運行變得均勻,就是系統(tǒng)時間內(nèi)運動的距離相同,此時keyTimes以及timingFunctions也是無效的.
簡單例子
CATransition
轉場動畫莺债,比 UIView 的轉場動畫具有更多的動畫效果。
CATransition的屬性:
-
type: 過渡動畫的類型
- kCATransitionFade 漸變
- kCATransitionMoveIn 覆蓋
- kCATransitionPush 推出
- kCATransitionReveal 揭開
私有動畫類型的值有:"cube"签夭、"suckEffect"齐邦、"oglFlip"、 "rippleEffect"第租、"pageCurl"措拇、"pageUnCurl"等等
-
subtype: 過渡動畫的方向
- kCATransitionFromRight 從右邊
- kCATransitionFromLeft 從左邊
- kCATransitionFromTop 從頂部
-
kCATransitionFromBottom 從底部
CASpringAnimation
CASpringAnimation是iOS9新加入動畫類型,是CABasicAnimation的子類慎宾,用于實現(xiàn)彈簧動畫丐吓。
CASpringAnimation的重要屬性:
- mass:質量(影響彈簧的慣性,質量越大璧诵,彈簧慣性越大汰蜘,運動的幅度越大)
- stiffness:彈性系數(shù)(彈性系數(shù)越大,彈簧的運動越快)
- damping:阻尼系數(shù)(阻尼系數(shù)越大之宿,彈簧的停止越快)
- initialVelocity:初始速率(彈簧動畫的初始速度大小族操,彈簧運動的初始方向與初始速率的正負一致,若初始速率為0,表示忽略該屬性)
- settlingDuration:結算時間(根據(jù)動畫參數(shù)估算彈簧開始運動到停止的時間色难,動畫設置的時間最好根據(jù)此時間來設置)
private func springAni() {
let ani = CASpringAnimation(keyPath: "bounds")
ani.mass = 10.0 //質量泼舱,影響圖層運動時的彈簧慣性,質量越大枷莉,彈簧拉伸和壓縮的幅度越大
ani.stiffness = 5000 //剛度系數(shù)(勁度系數(shù)/彈性系數(shù))娇昙,剛度系數(shù)越大,形變產(chǎn)生的力就越大笤妙,運動越快
ani.damping = 100.0//阻尼系數(shù)冒掌,阻止彈簧伸縮的系數(shù),阻尼系數(shù)越大蹲盘,停止越快
ani.initialVelocity = 5.0//初始速率股毫,動畫視圖的初始速度大小;速率為正數(shù)時,速度方向與運動方向一致召衔,速率為負數(shù)時铃诬,速度方向與運動方向相反
ani.duration = ani.settlingDuration
ani.toValue = NSValue(CGRect: view4.bounds)
ani.removedOnCompletion = false
ani.fillMode = kCAFillModeForwards
ani.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
view2.layer.addAnimation(ani, forKey: "boundsAni")
}
CAAnimationGroup
使用Group可以將多個動畫合并一起加入到層中,Group中所有動畫并發(fā)執(zhí)行苍凛,可以方便地實現(xiàn)需要多種類型動畫的場景,group動畫以數(shù)組表示趣席。
private func groupAni() {
let posAni = CABasicAnimation(keyPath: "position")
posAni.toValue = NSValue(CGPoint: CGPoint(x: 310, y: 400))
let boundAni = CABasicAnimation(keyPath: "bounds")
boundAni.toValue = NSValue(CGRect: CGRectMake(0, 0, 200, 200))
let colorAni = CABasicAnimation(keyPath: "backgroundColor")
colorAni.toValue = UIColor.redColor().CGColor
let groupAni = CAAnimationGroup()
groupAni.animations = [posAni, boundAni, colorAni]
groupAni.duration = 1.5
groupAni.fillMode = kCAFillModeForwards
groupAni.removedOnCompletion = false
groupAni.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
view1.layer.addAnimation(groupAni, forKey: "groupAni")
}
文中demo的地址:Github 動畫demo