iOS動畫詳解(學習動畫看這一篇就夠了)

動效設計一直是iOS平臺的優勢,良好的動效設計可以很好地提升用戶體驗。而動畫則是動效的基礎支撐。本動畫將從易到難逐步分析,從CABasicAnimation,UIBezierPath,CAShapeLayer三個方面完整的闡述iOS動畫的實現。最終的效果如下:

WuWeilogin.gif

例子來源與網絡,不是我寫的,我只是加上了詳細的注釋,方便大家理解(我只是代碼的搬運工...)。這個例子是CABasicAnimation,UIBezierPath,CAShapeLayer的綜合實現,如果能完全理解這個例子,相信其它的iOS動畫也難不倒你了。 demo下載地址

CABasicAnimation

一、概念

這個部分你需要了解以下概念: CALayer、CAAnimation、CAAnimationGroup

1、CALayer

CALayer是個與UIView很類似的概念,同樣有backgroundColor、frame等相似的屬性,我們可以將UIView看做一種特殊的CALayer。但實際上UIView是對CALayer封裝,在CALayer的基礎上再添加交互功能。UIView的顯示必須依賴于CALayer。我們同樣可以跟新建view一樣新建一個layer,然后添加到某個已有的layer上,同樣可以對layer調整大小、位置、透明度等。一般來說,layer可以有兩種用途:一是對view相關屬性的設置,包括圓角、陰影、邊框等參數,更詳細的參數請點擊這里;二是實現對view的動畫操控。 因此對一個view進行動畫,本質上是對該view的.layer進行動畫操縱。

2、CAAnimation

CAAnimation可以分為以下幾類:

CABasicAnimation基礎動畫,通過設定起始點,終點,時間,動畫會沿著你這設定點進行移動。可以看做特殊的CAKeyFrameAnimation

CAKeyframeAnimation關鍵幀動畫,可定制度比CABasicAnimation高,也是本系列的接下來的內容

CAAnimationGroup組動畫,支持多個CABasicAnimation或者CAKeyframeAnimation動畫同時執行

實例化

使用方法animationWithKeyPath:對 CABasicAnimation進行實例化,并指定Layer的屬性作為關鍵路徑進行注冊。

//圍繞y軸旋轉CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];

設定動畫的屬性和說明屬性說明

CABasicAnimation的屬性

transformAnima.fromValue = @(M_PI_2);
transformAnima.toValue = @(M_PI);
transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
transformAnima.autoreverses = YES;
transformAnima.repeatCount = HUGE_VALF;
transformAnima.beginTime = CACurrentMediaTime() + 2;

防止動畫結束后回到初始狀態只需設置removedOnCompletion、fillMode兩個屬性就可以了。

transformAnima.removedOnCompletion = NO;
transformAnima.fillMode = kCAFillModeForwards;

解釋:為什么動畫結束后返回原狀態?首先我們需要搞明白一點的是,layer動畫運行的過程是怎樣的?其實在我們給一個視圖添加layer動畫時,真正移動并不是我們的視圖本身,而是 presentation layer 的一個緩存。動畫開始時 presentation layer開始移動,原始layer隱藏,動畫結束時,presentation layer從屏幕上移除,原始layer顯示。這就解釋了為什么我們的視圖在動畫結束后又回到了原來的狀態,因為它根本就沒動過。

這個同樣也可以解釋為什么在動畫移動過程中,我們為何不能對其進行任何操作。

所以在我們完成layer動畫之后,最好將我們的layer屬性設置為我們最終狀態的屬性,然后將presentation layer 移除掉。

添加動畫

[self.imageView.layer addAnimation:transformAnima forKey:@"A"];

fillMode屬性的理解該屬性定義了你的動畫在開始和結束時的動作。默認值是 kCAFillModeRemoved。

kCAFillModeRemoved 這個是默認值,也就是說當動畫開始前和動畫結束后,動畫對layer都沒有影響,動畫結束后,layer會恢復到之前的狀態

kCAFillModeForwards 當動畫結束后,layer會一直保持著動畫最后的狀態

kCAFillModeBackwards 這個和kCAFillModeForwards是相對的,就是在動畫開始前,你只要將動畫加入了一個layer,layer便立即進入動畫的初始狀態。因為有可能出現fromValue不是目前layer的初始狀態的情況,如果fromValue就是layer當前的狀態,則這個參數就沒太大意義。

kCAFillModeBoth 理解了上面兩個,這個就很好理解了,這個其實就是上面兩個的合成.動畫加入后開始之前,layer便處于動畫初始狀態,動畫結束后layer保持動畫最后的狀態.

Animation Easing的使用

也即是屬性timingFunction值的設定,有種方式來獲取屬性值

(1)使用方法functionWithName:

這種方式很簡單,這里只是簡單說明一下取值的含義:

kCAMediaTimingFunctionLinear 傳這個值,在整個動畫時間內動畫都是以一個相同的速度來改變。也就是勻速運動。

kCAMediaTimingFunctionEaseIn 使用該值,動畫開始時會較慢,之后動畫會加速。

kCAMediaTimingFunctionEaseOut 使用該值,動畫在開始時會較快,之后動畫速度減慢。

kCAMediaTimingFunctionEaseInEaseOut 使用該值,動畫在開始和結束時速度較慢,中間時間段內速度較快。

動畫的實現

CABasicAnimation *positionAnima = [CABasicAnimation animationWithKeyPath:@"position.y"];
positionAnima.fromValue = @(self.imageView.center.y);
positionAnima.toValue = @(self.imageView.center.y-30);
positionAnima.timingFunction = [CAMediaTimingFunction functionWithName:
kCAMediaTimingFunctionEaseIn];
CABasicAnimation *transformAnima = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
transformAnima.fromValue = @(0);
transformAnima.toValue = @(M_PI);
transformAnima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
CAAnimationGroup *animaGroup = [CAAnimationGroup animation];
animaGroup.duration = 2.0f;
animaGroup.fillMode = kCAFillModeForwards;
animaGroup.removedOnCompletion = NO;
animaGroup.animations = @[positionAnima,transformAnima];[self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];

動畫開始和結束時的事件為了獲取動畫的開始和結束事件,需要實現協議

positionAnima.delegate = self;

代理方法實現

//動畫開始時- (void)animationDidStart:(CAAnimation *)anim{ 
NSLog(@"開始了");
}
//動畫結束時- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
 //方法中的flag參數表明了動畫是自然結束還是被打斷,比如調用了removeAnimationForKey:方法
或removeAnimationForKey方法,flag為NO,如果是正常結束,flag為YES。
 NSLog(@"結束了");
}

其實比較重要的是有多個動畫的時候如何在代理方法中區分不同的動畫兩種方式

方式一:

如果我們添加動畫的視圖是全局變量,可使用該方法。添加動畫時,我們使用了

[self.imageView.layer addAnimation:animaGroup forKey:@"Animation"];

所以,可根據key來區分不同的動畫

//動畫開始時- (void)animationDidStart:(CAAnimation *)anim{
 if ([anim isEqual:[self.imageView.layer animationForKey:@"Animation"]]) { 
NSLog(@"動畫組執行了");
 }
}

Note:把動畫存儲為一個屬性然后再回調中比較,用來判定是哪個動畫是不可行的。應為委托傳入的動畫參數是原始值的一個深拷貝,不是同一個值

方式二

添加動畫的視圖是局部變量時,可使用該方法添加動畫給動畫設置key-value對

[positionAnima setValue:@"PositionAnima" forKey:@"AnimationKey"];
[transformAnima setValue:@"TransformAnima" forKey:@"AnimationKey"];

所以,可以根據key中不同的值來進行區分不同的動畫

//動畫結束時- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
 if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"PositionAnima"]) { 
        NSLog(@"位置移動動畫執行結束");
 } else if ([[anim valueForKey:@"AnimationKey"]isEqualToString:@"TransformAnima"]){ 
        NSLog(@"旋轉動畫執行結束");
 }}

一些常用的animationWithKeyPath值的總結

animationWithKeyPath值

UIBezierPath

使用UIBezierPath可以創建基于矢量的路徑,此類是Core Graphics框架關于路徑的封裝。使用此類可以定義簡單的形狀,如橢圓、矩形或者有多個直線和曲線段組成的形狀等。

UIBezierPath是CGPathRef數據類型的封裝。如果是基于矢量形狀的路徑,都用直線和曲線去創建。我們使用直線段去創建矩形和多邊形,使用曲線去創建圓弧(arc)、圓或者其他復雜的曲線形狀。

87FE4D73-A87A-4B8C-9A0E-73941FA532EC.png

+ (instancetype)bezierPath;

這個使用比較多,因為這個工廠方法創建的對象,我們可以根據我們的需要任意定制樣式,可以畫任何我們想畫的圖形。

+ (instancetype)bezierPathWithRect:(CGRect)rect;

這個工廠方法根據一個矩形畫貝塞爾曲線。

+ (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

這個工廠方法根據一個矩形畫內切曲線。通常用它來畫圓或者橢圓。

+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
+ (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;

第一個工廠方法是畫矩形,但是這個矩形是可以畫圓角的。第一個參數是矩形,第二個參數是圓角大小。

第二個工廠方法功能是一樣的,但是可以指定某一個角畫成圓角。像這種我們就可以很容易地給UIView擴展添加圓角的方法了。

+ (instancetype)bezierPathWithArcCenter:(CGPoint)center
 radius:(CGFloat)radius
 startAngle:(CGFloat)startAngle
 endAngle:(CGFloat)endAngle
 clockwise:(BOOL)clockwise;

這個工廠方法用于畫弧,參數說明如下:

center: 弧線中心點的坐標

radius: 弧線所在圓的半徑

startAngle: 弧線開始的角度值

endAngle: 弧線結束的角度值

clockwise: 是否順時針畫弧線

- (void)closePath;//閉合弧線

CAShapeLayer

CAShapeLayer是在其坐標系統內繪制貝塞爾曲線(UIBezierPath)的。因此,使用CAShapeLayer需要與UIBezierPath一起使用。

它有一個path屬性,而UIBezierPath就是對CGPathRef類型的封裝,因此這兩者配合起來使用才可以的哦!

CAShapeLayer與UIBezierPath的關系:

CAShapeLayer中shape代表形狀的意思,所以需要形狀才能生效

貝塞爾曲線可以創建基于矢量的路徑,而UIBezierPath類是對CGPathRef的封裝

貝塞爾曲線給CAShapeLayer提供路徑,CAShapeLayer在提供的路徑中進行渲染。路徑會閉環,所以繪制出了Shape

用于CAShapeLayer的貝塞爾曲線作為path,其path是一個首尾相接的閉環的曲線,即使該貝塞爾曲線不是一個閉環的曲線

CAShapeLayer與UIBezierPath畫圓

- (CAShapeLayer *)drawCircle {
CAShapeLayer *circleLayer = [CAShapeLayer layer];
 // 指定frame,只是為了設置寬度和高度
 circleLayer.frame = CGRectMake(0, 0, 200, 200);
 // 設置居中顯示
 circleLayer.position = self.view.center; 
 // 設置填充顏色
 circleLayer.fillColor = [UIColor clearColor].CGColor;
 // 設置線寬
 circleLayer.lineWidth = 2.0;
 // 設置線的顏色
 circleLayer.strokeColor = [UIColor redColor].CGColor;
 // 使用UIBezierPath創建路徑
 CGRect frame = CGRectMake(0, 0, 200, 200);
 UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:frame]; 
// 設置CAShapeLayer與UIBezierPath關聯
 circleLayer.path = circlePath.CGPath;
 // 將CAShaperLayer放到某個層上顯示
 [self.view.layer addSublayer:circleLayer]; return circleLayer;}

登錄例子下載地址:

demo下載地址

參考資料:

iOS 動畫效果:Core Animation & 非死book

拍電影與CABasicAnimation

標哥的技術博客

CABasicAnimation使用總結

蘋果文檔

放肆的使用UIBezierPath和CAShapeLaye

 

來自:http://www.jianshu.com/p/79a0b3c7086c

 

 本文由用戶 zody1933 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!