iOS出門必備之CoreAnimation(核心動畫)
阿新 • • 發佈:2018-12-23
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)
1.需要注意的是
- CAAnimation是所有動畫類的父類,但是它不能直接使用,應該使用它的子類
- CAPropertyAnimation也是不能直接使用的,也要使用它的子類
- 能用的動畫類只剩下4個:CABasicAnimation、CAKeyframeAnimation、CATransition、CAAnimationGroup
2.常用屬性
1). removedOnCompletion
:預設為true,代表動畫執行完畢後就從圖層上移除
- 圖形會恢復到動畫執行前的狀態。如果想讓圖層保持顯示動畫執行後的狀態,那就設定為false,不過還要設定fillMode
為kCAFillModeForwards
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
CABasicAnimation
是CAPropertyAnimation
的子類,使用它可以實現一些基本的動畫效果,它可以讓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的anchorPoint
和position
起作用。如果設定了path,那麼values將被忽略keyTimes
:可以為對應的關鍵幀指定對應的時間點,其取值範圍為0到1.0,keyTimes中的每一個時間值都對應values中的每一幀。如果沒有設定keyTimes,各個關鍵幀的時間是平分的calculationMode
: 該屬性決定了物體在每個子路徑下是跳著走還是勻速走,跟timeFunctions
屬性有點類似kCAAnimationLinear
預設值,表示當關鍵幀為座標點的時候,關鍵幀之間直接直線相連進行插值計算;kCAAnimationDiscrete
離散的,就是不進行插值計算,所有關鍵幀直接逐個進行顯示;kCAAnimationPaced
使得動畫均勻進行,而不是按keyTimes
設定的或者按關鍵幀平分時間,此時keyTimes
和timingFunctions無效;</li>
kCAAnimationCubic
<li>對關鍵幀為座標點的關鍵幀進行圓滑曲線相連後插值計算,對於曲線的形狀還可以通過
tensionValues,continuityValues,biasValues來進行調整自定義主要目的是使得執行的軌跡變得圓滑;</li>
kCAAnimationCubicPaced
<li>看這個名字就知道和
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
CATransition
是CAAnimation
的子類,用於做轉場動畫,能夠為layer層提供移出螢幕和移入螢幕的動畫效果。- iOS比Mac OS X的轉場動畫效果少一點
UINavigationController
就是通過CATransition
實現了將控制器的檢視推入螢幕的動畫效果 - 動畫屬性:
type
:動畫過渡型別subtype
:動畫過渡方向startProgress
:動畫起點(在整體動畫的百分比)endProgress
:動畫終點(在整體動畫的百分比)
1. type
和subtype
屬性說明
/* 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 //相機鏡頭關上效果(不支援過渡方向)
效果參考
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
- 如有不足之處,還望路過的大神多多指教