代碼地址以下:
http://www.demodashi.com/demo/11603.htmlhtml
關於實現一個iOS動畫,若是簡單的,咱們能夠直接調用UIView
的代碼塊來實現,雖然使用UIView
封裝的方法很方便,可是這隻能用於一些簡答的動畫,若是是一些複雜的動畫呢?這就不得不去研究下核心動畫Core Animation
(包含在Quartz Core
框架中)了。這這以前咱們必須瞭解,CALayer
就包含在Quartz Core
框架中,這是一個跨平臺的框架,既能夠用在iOS中又能夠用在Mac OS X中。在使用Core Animation
開發動畫的本質就是將CALayer
中的內容轉化爲位圖從而供硬件操做,因此要熟練掌握動畫操做必須熟悉CALayer
,關於CALayer
就不在這裏講了。今天主要是分析核心動畫,iOS 中的核心動畫又分爲下面幾種:基礎動畫、關鍵幀動畫、動畫組、轉場動畫、彈簧動畫。下面咱們先來了解下各個動畫之間的關係spring
@interface CAAnimation : NSObject <NSCoding, NSCopying, CAMediaTiming, CAAction> { @private void *_attr; uint32_t _flags; }
這是核心動畫的基類,不能直接使用,主要負責動畫的時間、速度等,從上面能夠看出是準守CAMediaTiming
協議的。併發
屬性動畫的基礎類,繼承自CAAnimation
,不能直接使用。何謂屬性動畫呢?即經過修改屬性值就能夠產生動畫效果。框架
動畫組,繼承自CAAnimation
,顧名思義就是一種組合動畫,能夠經過動畫組來進行全部動畫行爲的統一控制,組中全部動畫效果能夠併發執行。函數
轉場動畫,繼承自CAAnimation
,主要是經過濾鏡來進行動畫的效果設置動畫
基礎動畫,繼承自CAPropertyAnimation
,經過屬性控制動畫的參數,只要初始狀態和結束狀態ui
關鍵幀動畫,繼承自CAPropertyAnimation
,也是經過屬性控制動畫參數,可是與基礎動畫不一樣的是有多個控制狀態,而且能夠經過path
來實現動畫url
彈簧動畫,是在iOS 9中引入的,繼承自CABasicAnimation
,用於製做彈簧動畫3d
在使用動畫以前,先補充個知識點---UIBezierPath, 這在動畫使用的過程當中會常常用到code
要使用核心動畫,咱們必須先了解下其屬性,這裏咱們先看其遵照的協議<CAMediaTiming>
屬性 | 說明 |
---|---|
beginTime | 指定動畫開始的時間。開始延遲幾秒的話,設置爲CACurrentMediaTime() + 秒數 的方式便可 |
duration | 動畫的時長 |
speed | 動畫的速度 |
timeOffset | 詳細說明 |
repeatCount | 動畫重複的次數,若是要一直持續設置爲HUGE_VALF 便可 |
repeatDuration | 設置動畫的時間。在該時間內動畫一直執行,不計次數 |
autoreverses | 動畫結束時是否執行逆動畫 |
fillMode | 分四種狀況,分別爲kCAFillModeForwards 、kCAFillModeBackwards 、kCAFillModeBoth 、kCAFillModeRemoved ,決定當前對象在非active時間段的行爲,好比動畫開始以前或者動畫結束之 |
CAAnimation
屬性
屬性 | 說明 |
---|---|
timingFunction | 速度控制函數,控制動畫運行的節奏 |
removedOnCompletion | 默認爲YES ,表明動畫執行完畢後就從圖層上移除,圖形會恢復到動畫執行前的狀態。若是想讓圖層保持顯示動畫執行後的狀態,那就設置爲NO ,不過還要設置fillMode 爲kCAFillModeForwards |
關於fillMode
的四種狀況:
kCAFillModeRemoved
默認值,動畫結束後,layer會恢復到以前的狀態
kCAFillModeForwards
當動畫結束後,layer會一直保持着動畫最後的狀態,而removedOnCompletion
的默認屬性值是 YES
,因此爲了使動畫結束以後layer保持結束狀態,應將removedOnCompletion
設置爲NO
。
kCAFillModeBackwards
在動畫開始前,只須要將動畫加入了一個layer,layer便當即進入動畫的初始狀態並等待動畫開始。
kCAFillModeBoth
這個其實就是上面兩個的合成,動畫加入後開始以前,layer便處於動畫初始狀態,動畫結束後layer保持動畫最後的狀態
關於速度CAMediaTimingFunction
控制的四種狀況
kCAMediaTimingFunctionLinear
(線性):勻速,給你一個相對靜態的感受
kCAMediaTimingFunctionEaseIn
(漸進):動畫緩慢進入,而後加速離開
kCAMediaTimingFunctionEaseOut
(漸出):動畫全速進入,而後減速的到達目的地
kCAMediaTimingFunctionEaseInEaseOut
(漸進漸出):動畫緩慢的進入,中間加速,而後減速的到達目的地。這個是默認的動畫行爲。
從上圖中咱們知道,屬性動畫是繼承自核心動畫,在其API中咱們能夠看到有以下函數和屬性
+ (instancetype)animationWithKeyPath:(nullable NSString *)path; @property(nullable, copy) NSString *keyPath;
其中都有keyPath
,這又是什麼呢?這就是屬性動畫與動畫組和轉場動畫不一樣之處。經過指定CALayer
的一個屬性名稱爲keyPath
(NSString類型),而且對CALayer
的這個屬性的值進行修改,達到相應的動畫效果。好比,指定@"opacity"
爲keyPath
,就修改CALayer
的opacity
屬性的值,以達到透明度變化的動畫效果
下面咱們列舉一些經常使用的animationWithKeyPath
值
經常使用值 | 說明 | 使用方式 |
---|---|---|
transform | 3D變換 | [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0)] ,直接進行3D變換 |
transform.scale | 縮放 | @(1.5) ,放大1.5倍 |
transform.scale.x | 寬度縮放 | @(1.5) ,寬放大1.5倍 |
transform.scale.y | 高度縮放 | @(1.5) ,高放大1.5倍 |
transform.rotation.x | 圍繞x軸旋轉 | @(M_PI) ,x軸旋轉180度 |
transform.rotation.y | 圍繞y軸旋轉 | @(M_PI) ,y軸旋轉180度 |
transform.rotation.z | 圍繞z軸旋轉 | @(M_PI) ,z軸旋轉180度 |
position | 位置(中心點的改變) | [NSValue valueWithCGPoint:CGPointMake(100, 100)] ,中心點變爲(100,100) |
opacity | 透明度 | @(0.5) ,透明度變爲0.5 |
bounds | 大小的改變 中心點保持不變 | [NSValue valueWithCGRect:CGRectMake(0, 0, 300, 300)] ,大小變爲(300,300) |
cornerRadius | 圓角的設置 | @(5) ,圓角設置爲5 |
backgroundColor | 背景顏色變換 | (id)[UIColor redColor].CGColor ,背景改成紅色 |
contents | 能夠改變layer展現的圖片 | (id)[UIImage imageNamed:@"12.png"].CGImage ,將UIView的展現圖片改成12.png |
strokeStart | 從起始點開始變化 | fromValue = 0 ,toValue = 1 ,爲CAShapeLayer 的屬性 |
strokeEnd | 從結束的位置開始變化 | fromValue = 1 ,toValue = 0.5 ,爲CAShapeLayer 的屬性 |
path | 根據路徑來改變 | fromValue = (__bridge id)(start.CGPath); ,toValue = (__bridge id)((end.CGPath)) |
基礎動畫是繼承自屬性動畫,因此在使用的時候,咱們最主要的就是經過屬性來控制動畫,好比設置初始值、結束值,固然還有核心動畫的其餘屬性。
- (void)showAnimation { CAShapeLayer * circle = [CAShapeLayer layer]; circle.frame = self.view.bounds; // UIBezierPath * circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)) radius:self.normalSize.width/2 -1 startAngle:0 endAngle:2*M_PI clockwise:YES]; circle.path = circlePath.CGPath; circle.strokeColor = [UIColor blueColor].CGColor; circle.fillColor = nil; [self.view.layer addSublayer:circle]; //經過圓的strokeStart 改變來進行改變 CABasicAnimation * strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"]; strokeStartAnimation.fromValue = @(0.2); strokeStartAnimation.toValue = @(0); //經過圓的strokeEnd 改變來進行改變 CABasicAnimation * strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; strokeEndAnimation.fromValue = @0.5; strokeEndAnimation.toValue = @(1.0); //經過圓的transform.rotation.z 改變來進行改變 CABasicAnimation * rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; rotationAnimation.fromValue = @(0); rotationAnimation.toValue = @(-M_PI * 2); //組合動畫 CAAnimationGroup * group = [CAAnimationGroup animation]; group.duration = 5; group.removedOnCompletion = NO; group.fillMode = kCAFillModeForwards; group.animations = @[strokeStartAnimation,strokeEndAnimation,rotationAnimation]; [circle addAnimation:group forKey:nil]; }
在上面的基礎動畫代碼中,我只用了最基礎的兩個屬性,fromValue
和toValue
,而其它屬性呢?因爲後面用到了動畫組,因此講其它屬性在動畫組進行了設置。[circle addAnimation:group forKey:nil];
這句代碼中,key
我設置的爲nil
,若是不設置爲nil
的時候,是什麼意思呢?這個其實就是咱們的動畫設置了一個key
,能夠用來區別是哪個動畫,在後面我會舉例說明。
上面代碼對應的效果以下:
關鍵幀動畫也是屬性動畫,與基礎動畫最主要不一樣的是在兩個參數上,NSArray *values
和CGPathRef path
,經過這個咱們能夠知曉,關鍵幀動畫能夠設置多個控制狀態
以下:
//用value的方式進行展現動畫 - (void)showKeyFrameAnimationWithValues { CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; NSValue *key1 = [NSValue valueWithCGPoint:_beginPoint]; NSValue *key2 = [NSValue valueWithCGPoint:CGPointMake(100, 100)]; NSValue *key3 = [NSValue valueWithCGPoint:CGPointMake(150, 50)]; NSValue *key4 = [NSValue valueWithCGPoint:CGPointMake(300, 250)]; animation.values = @[key1,key2,key3,key4]; animation.duration = 5.0; animation.delegate = (id)self; // animation.autoreverses = true;//是否按路徑返回 // animation.repeatCount = HUGE;//是否重複執行 animation.removedOnCompletion = NO;//執行後移除動畫 animation.fillMode = kCAFillModeForwards; //存儲位置 [animation setValue:key4 forKey:@"keyframeAnimationLocation"]; [_fishImageView.layer addAnimation:animation forKey:@"keyframeAnimation_fish"]; }
並且還能夠設置路徑,這也是其最大的特色
以下:
//用CGPathRef的方式進行展現動畫 - (void)showKeyFrameAnimationWithPath { CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:_fishImageView.layer.position]; //三次貝塞爾曲線 [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(150, 400) controlPoint2:CGPointMake(230, -100)]; animation.path = path.CGPath; animation.duration = 5.0; animation.delegate = (id)self; [_fishImageView.layer addAnimation:animation forKey:@"keyframeAnimation_path_fish"]; }
效果以下,這裏就暫時只給出values
的效果
在上面的兩個方法中,對layer
設置了兩個不一樣的key
,分別爲keyframeAnimation_fish
和keyframeAnimation_path_fish
前面我提到過,經過這個能夠判斷是哪種動畫,這裏咱們在動畫結束的地方進行區分一下
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if ([anim isEqual:[_fishImageView.layer animationForKey:@"keyframeAnimation_fish"]]) { // [CATransaction begin]; // //禁用隱式動畫 // [CATransaction setDisableActions:YES]; _fishImageView.layer.position = [[anim valueForKey:@"keyframeAnimationLocation"] CGPointValue]; // //提交事務 // [CATransaction commit]; } else if ([anim isEqual:[_fishImageView.layer animationForKey:@"keyframeAnimation_path_fish"]]) { } }
動畫組其實很簡單,就是將許多動畫組合在一塊兒
在上面的基礎動畫中,我也用到了動畫組,下面再貼上一組動畫組合效果
//發射 - (void)launchAnimation { CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"]; animation1.fromValue = @(1.0); animation1.toValue = @(1.5); CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"]; animation2.fromValue = @(1.0); animation2.toValue = @(1.5); CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"position"]; animation3.fromValue = [NSValue valueWithCGPoint:_ballLayer.position]; animation3.toValue = [NSValue valueWithCGPoint:CGPointMake(self.view.frame.size.width - (30 * 1.3)/2.0 , _ballLayer.position.y - 200)]; CAAnimationGroup *anima = [CAAnimationGroup animation]; anima.animations = @[animation1, animation2,animation3]; anima.duration = 1.0; anima.delegate = (id)self; anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; anima.fillMode = kCAFillModeForwards; anima.removedOnCompletion = NO; [_ballLayer addAnimation:anima forKey:@"group_launch"]; }
在後面的demo裏有完整的代碼,這裏只是截取了部分代碼
效果以下:
在瞭解轉場動畫以前,咱們先了解其兩個參數,經過這些參數,咱們就能很清楚的瞭解其效果
type
轉場類型轉場動畫類型 | 說明 | 常量 | 是否支持方向設置 |
---|---|---|---|
公開API | |||
fade | 淡出 | kCATransitionFade |
是 |
movein | 新視圖移動到舊視圖上面 | kCATransitionMoveIn |
是 |
push | 新視圖退出舊視圖 | kCATransitionPush |
是 |
reveal | 移開舊視圖顯示新的 | kCATransitionReveal |
是 |
私有API | 蘋果未公開的類型,可是目前仍是能夠用的 | 私有API只能經過下面的字符串進行訪問 | |
cube | 立體翻轉 | 是 | |
oglFlip | 翻轉 | 是 | |
suckEffect | 收縮 | 否 | |
rippleEffect | 水滴波紋效果 | 否 | |
pageCurl | 向上翻頁效果 | 是 | |
pageUnCurl | 向下翻頁效果 | 是 | |
cameraIrisHollowOpen | 攝像頭打開效果 | 否 | |
cameraIrisHollowClose | 攝像頭關閉效果 | 否 |
subtype
動畫子類型屬性 | 說明 |
---|---|
kCATransitionFromRight | 從右 |
kCATransitionFromLeft | 從左 |
kCATransitionFromTop | 從頂部 |
kCATransitionFromBottom | 從底部 |
在瞭解上面兩個屬性後,對應轉場動畫,就差很少了
部分代碼以下
- (void)transition:(BOOL)next { CATransition *transition = [CATransition animation]; transition.type = @"cube"; if (next) { transition.subtype = kCATransitionFromLeft; } else { transition.subtype = kCATransitionFromRight; } transition.duration = 1.0; _imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",(long)_currentIndex]]; [_imageView.layer addAnimation:transition forKey:@"transitionAnimation"]; }
效果以下
彈簧動畫是在iOS 9後纔出現的,在這以前,咱們能夠經過下面的方法來實現
[UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{ } completion:nil];
iOS 9後蘋果公開了這一API,咱們先對其中的屬性進行分析,由於代碼中有註釋,因此就直接貼一部分代碼
//質量,影響圖層運動時的彈簧慣性,質量越大,彈簧拉伸和壓縮的幅度越大 positionAnimation.mass = 0.1; //阻尼係數,阻止彈簧伸縮的係數,阻尼係數越大,中止越快 positionAnimation.damping = 2; //剛度係數(勁度係數/彈性係數),剛度係數越大,形變產生的力就越大,運動越快 positionAnimation.stiffness = 50; //初始速率,動畫視圖的初始速度大小 //速率爲正數時,速度方向與運動方向一致,速率爲負數時,速度方向與運動方向相反 positionAnimation.initialVelocity = -10;
關於彈簧動畫,我也寫了一個例子,效果以下
關於核心動畫,差很少就簡單的介紹這麼點,若有什麼不對的還望各位多多指教,不甚感激。iOS 核心動畫 Core Animation淺談
代碼地址以下:
http://www.demodashi.com/demo/11603.html
注:本文著做權歸做者,由demo大師代發,拒絕轉載,轉載須要做者受權