iOS核心動(dòng)畫詳解2

上一篇iOS核心動(dòng)畫詳解1說的很清楚了,那么下面詳細(xì)介紹。

那么這里,將介紹上面所提到的7個(gè)類1個(gè)協(xié)議具體的細(xì)節(jié)。

1:CAMediaTiming協(xié)議

CAMediaTiming 協(xié)議中定義了時(shí)間,速度,重復(fù)次數(shù)等。屬性定義如下:

  • beginTime-> 用來設(shè)置動(dòng)畫延時(shí),若想延遲1秒,就設(shè)置為CACurrentMediaTime()+1,其中CACurrentMediaTime()為圖層當(dāng)前時(shí)間。
  • duration -> 動(dòng)畫的持續(xù)時(shí)間。
  • speed-> 動(dòng)畫速率,決定動(dòng)畫時(shí)間的倍率。當(dāng)speed為2時(shí),動(dòng)畫時(shí)間為設(shè)置的duration的1/2。
  • timeOffset -> 動(dòng)畫時(shí)間偏移量。比如設(shè)置動(dòng)畫時(shí)長(zhǎng)為3秒,當(dāng)設(shè)置timeOffset為1.5時(shí),當(dāng)前動(dòng)畫會(huì)從中間位置開始,并在到達(dá)指定位置時(shí),走完之前跳過的前半段動(dòng)畫。
  • repeatCount -> 動(dòng)畫的重復(fù)次數(shù)。
  • repeatDuration -> 動(dòng)畫的重復(fù)時(shí)間。
  • autoreverses -> 動(dòng)畫由初始值到最終值后,是否反過來回到初始值的動(dòng)畫。如果設(shè)置為YES,就意味著動(dòng)畫完成后會(huì)以動(dòng)畫的形式回到初始值。
  • fillMode -> 決定當(dāng)前對(duì)象在非動(dòng)畫時(shí)間段的行為.比如動(dòng)畫開始之前,動(dòng)畫結(jié)束之后。
    fillMode詳細(xì)說明:試想這樣一個(gè)問題:在beginTime非0(即動(dòng)畫未真正執(zhí)行之前),以及removeOnCompletion被設(shè)置為NO的動(dòng)畫結(jié)束時(shí),我們會(huì)遇到這樣一個(gè)問題:被設(shè)置動(dòng)畫的屬性應(yīng)該是什么值?
    一種可能是屬性與動(dòng)畫沒被添加之前保持一致,還有一種可能是保持動(dòng)畫開始之前那一幀或者動(dòng)畫結(jié)束那一幀,這就是所謂的填充。
    CAMediaTiming的fillMode用來控制填充效果,它是一個(gè)NSString類型,有四種常量可供使用:
    kCAFillModeRemoved (default) NSString 默認(rèn)值,動(dòng)畫開始前和結(jié)束后,動(dòng)畫對(duì)圖層都沒有影響,圖層依然保持初始值
    kCAFillModeForwards NSString 動(dòng)畫結(jié)束后,圖層一直保持動(dòng)畫后的最終狀態(tài)
    kCAFillModeBackwards NSString 動(dòng)畫開始前,只要加入動(dòng)畫就會(huì)處于動(dòng)畫的初始狀態(tài)
    kCAFillModeBoth NSString 綜合了kCAFillModeForwards與kCAFillModeBackwards特性;
    (動(dòng)畫加入圖層到真正執(zhí)行動(dòng)畫的時(shí)間段里,圖層保持動(dòng)畫初始狀態(tài);動(dòng)畫結(jié)束之后保持動(dòng)畫最終狀態(tài))

其實(shí)不只是CAAnimation遵循CAMediaTiming協(xié)議,熟悉底層結(jié)構(gòu)的小伙伴們應(yīng)該知道CALayer也遵循這個(gè)協(xié)議,所有在一定程度上我們可以通過控制layer本身的協(xié)議屬性來控制動(dòng)畫節(jié)奏。

2:CAAnimation核心動(dòng)畫基礎(chǔ)類

CAAnimation核心動(dòng)畫基礎(chǔ)類,不能直接使用。除了CAMediaTiming協(xié)議中的方法,增加了CAAnimationDelegate的代理屬性等。關(guān)于它的定義如下:

@interface CAAnimation : NSObject<NSSecureCoding, NSCopying, CAMediaTiming, CAAction>

@property(nullable, strong) CAMediaTimingFunction *timingFunction;
@property(nullable, strong) id <CAAnimationDelegate> delegate;
@property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;

@end

屬性如下:

  • timingFunction -> 動(dòng)畫緩沖屬性。動(dòng)畫實(shí)際上就是在一段時(shí)間內(nèi)隨著某個(gè)特定速率執(zhí)行變化的過程,現(xiàn)實(shí)中的任何物體都會(huì)在運(yùn)動(dòng)中經(jīng)歷加速或者減速的過程,而不是速度驟變;因此,CoreAnimation也內(nèi)嵌了一系列標(biāo)準(zhǔn)的緩沖函數(shù)來使動(dòng)畫看起來更平滑自然,這就是我們要說到的動(dòng)畫緩沖??刂苿?dòng)畫的節(jié)奏。系統(tǒng)提供的包括:
  • kCAMediaTimingFunctionLinear(默認(rèn),勻速執(zhí)行動(dòng))
  • kCAMediaTimingFunctionEaseIn(慢進(jìn)快出)
  • kCAMediaTimingFunctionEaseOut(快進(jìn)慢出)
  • kCAMediaTimingFunctionEaseInEaseOut (慢進(jìn)慢出,中間加速)
  • kCAMediaTimingFunctionDefault (默認(rèn)),當(dāng)然也可通過自定義創(chuàng)建CAMediaTimingFunction。
    注意:KCAMediaTimingFuncationDefault相比KCAMediaTimingFuncationEaseInEaseOut的加速和減速過程稍微有些慢,兩者區(qū)別很難察覺;可能蘋果也覺得它更適合用于隱式動(dòng)畫,就作為了隱式動(dòng)畫的默認(rèn)效果;但是創(chuàng)建顯式的CAAnimation時(shí),KCAMediaTimingFuncationLinear才是默認(rèn)效果而非KCAMediaTimingFuncationDefault;
  • removedOnCompletion -> 是否讓圖層保持顯示動(dòng)畫執(zhí)行后的狀態(tài),默認(rèn)為YES,也就是動(dòng)畫執(zhí)行完畢后從涂層上移除,恢復(fù)到執(zhí)行前的狀態(tài),如果設(shè)置為NO,并且設(shè)置fillModekCAFillModeForwards,則保持動(dòng)畫執(zhí)行后的狀態(tài)。
  • delegate -> 代理。

CAAnimationdelegate代理方法如下幾個(gè):

//動(dòng)畫開始時(shí)執(zhí)行的回調(diào)
- (void)animationDidStart:(CAAnimation *)anim;
//動(dòng)畫結(jié)束后執(zhí)行的回調(diào)
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

removedOnCompletion屬性默認(rèn)為YES,表示動(dòng)畫完成后就會(huì)從圖層上移除,圖層也會(huì)恢復(fù)到動(dòng)畫執(zhí)行前的狀態(tài);當(dāng)其修改為NO時(shí),那么圖層將會(huì)保持動(dòng)畫結(jié)束后的狀態(tài),此時(shí)的fillMode屬性也將生效;

另外,removedOnCompletion設(shè)置為NO時(shí),直到我們手動(dòng)移除動(dòng)畫,否則動(dòng)畫將不會(huì)自動(dòng)釋放;所以通常我們此時(shí)會(huì)給動(dòng)畫添加一個(gè)非空的鍵,這樣可以在不需要?jiǎng)赢嫷臅r(shí)候把它從圖層上移除;

3:CAPropertyAnimation 屬性動(dòng)畫

CAPropertyAnimation 是一個(gè)抽象類,不可直接使用。不能直接用于實(shí)現(xiàn)CALayer動(dòng)畫操作,但是它的類定義中增加用于設(shè)置CALayer可被實(shí)現(xiàn)動(dòng)畫的屬性keyPath。

方法+ (instancetype)animationWithKeyPath:(NSString *)path;該方法僅需要一個(gè)參數(shù),該參數(shù)只是一個(gè)字符串的值,指定CALayer的動(dòng)畫屬性名,該設(shè)置屬性動(dòng)畫控制CALayer的哪個(gè)動(dòng)畫屬性持續(xù)的改變。

屬性:具體如下:

  • keyPath -> CALayer的某個(gè)屬性名,并通過這個(gè)屬性的值進(jìn)行修改,達(dá)到相應(yīng)的動(dòng)畫效果。
  • additive -> 屬性動(dòng)畫是否以當(dāng)前動(dòng)畫效果為基礎(chǔ),默認(rèn)為NO。
  • cumulative -> 指定動(dòng)畫是否為累加效果,默認(rèn)為NO。
  • valueFunction -> 此屬性配合CALayer的transform屬性使用。
    CABasicAnimation基礎(chǔ)動(dòng)畫,通過keyPath對(duì)應(yīng)屬性進(jìn)行控制,需要設(shè)置fromValue以及toValue。添加屬性如下:
  • fromValue -> keyPath相應(yīng)屬性的初始值。
  • toValue -> keyPath相應(yīng)屬性的結(jié)束值。
  • byValue -> 在不設(shè)置toValue時(shí),toValue = fromValue + byValue,也就是在當(dāng)前的位置上增加多少。
  • affineTransform:該屬性值指定一個(gè)(- (CGAffineTransform)affineTransform;)CGAffineTransform對(duì)象(變換矩陣),該對(duì)象代表CALayer執(zhí)行X,Y兩個(gè)維度(也就是平面)上的旋轉(zhuǎn),縮放,位移,斜切,鏡像等變換矩陣。
  • transform: 該屬性值指定一個(gè)CATransform3D對(duì)象,該對(duì)象代表對(duì)CALayer執(zhí)行X,Y,Z三個(gè)維度(三維空間)中的旋轉(zhuǎn),縮放,位移,斜切,鏡像等變換矩陣。很明顯如果只是對(duì)CALayer進(jìn)行平面上的變換,指定普通的affineTransform屬性即可,如果要對(duì)CALayer執(zhí)行三維空間的變化,則需要指定transform屬性。
 CATransform3D:就是下面的結(jié)構(gòu)體
struct CATransform3D
    {
        CGFloat m11, m12, m13, m14;
        CGFloat m21, m22, m23, m24;
        CGFloat m31, m32, m33, m34;
        CGFloat m41, m42, m43, m44;
    };

其中(m11, m12, m13,m21, m22, m23,m31, m32, m33)將會(huì)組成變換矩陣,m14,m24,m34,m44只是占位符,通常m14,m24,m34會(huì)設(shè)置為0.m14設(shè)置為1.假如變換前的店坐標(biāo)為(x,y,z),與該矩陣相乘后得到變換后該點(diǎn)的坐標(biāo)。按矩陣相乘算法:
[x,y,z].(m11,m12,m13
m21,m22,m23
m31,m32,m33)=(xm11+ym21+zm31 xm12+ym22+zm32 xm13+ym23+z*m33)

上面公司計(jì)算出來的坐標(biāo)還要加上tx,ty,tz這3個(gè)X,Y,Z方向的偏移量。因此對(duì)于點(diǎn)(x,y,z)經(jīng)過CATransform3D變換后,該點(diǎn)的實(shí)際坐標(biāo)為(xm11+ym21+zm31+tx xm12+ym22+zm32+ty xm13+ym23+z*m33+tz).

一般來說可以使用 Core Animation提供的如下函數(shù)來創(chuàng)建三維變換矩陣。

CATransform3DIsIdentity(CATransform3D t) :判斷t矩陣是否為單位矩陣
CATransform3DEqualToTransform(CATransform3D a, CATransform3D b) :判斷兩個(gè)變換矩陣是否相等
CATransform3DMakeTranslation(CGFloat tx, CGFloat ty, CGFloat tz):創(chuàng)建在x方向上移動(dòng)tx,在y方向上移動(dòng)ty,在z方向上移動(dòng)tz的變換矩陣。
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz):創(chuàng)建在x方向上縮放tx,在y方向上縮放ty,在z方向上縮放tz的變換矩陣。
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z) :創(chuàng)建基于指定旋轉(zhuǎn)軸旋轉(zhuǎn)angle弧度的變換,其中x,y,z用于確定旋轉(zhuǎn)軸的方向。比如 (1,0,0)指定旋轉(zhuǎn)軸為x軸,(1,1,0)指定以x,y軸夾角的中線為旋轉(zhuǎn)軸。
CATransform3DTranslate(CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz):以已有t變換矩陣為基礎(chǔ)進(jìn)行位移變換。
CATransform3DScale(CATransform3D t, CGFloat sx, CGFloat sy, CGFloat sz) : 以已有t變換矩陣為基礎(chǔ)進(jìn)行縮放變換。
CATransform3DRotate(CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z):以已有t變換矩陣為基礎(chǔ)進(jìn)行旋轉(zhuǎn)變換。
CATransform3DConcat(CATransform3D a, CATransform3D b):對(duì)a變換矩陣進(jìn)行累加
CATransform3DInvert(CATransform3D t):對(duì)已有的t變換矩陣執(zhí)行反轉(zhuǎn)。
CATransform3DMakeAffineTransform(CGAffineTransform m):將CGAffineTransform矩陣包裝成為CATransform3D變換矩陣,該CATransform3D也只有x,y維度變換。
CATransform3DIsAffine(CATransform3D t) :如果t變換矩陣只有一個(gè)CGAffineTransform矩陣,則改函數(shù)返回YES.
CATransform3DGetAffineTransform(CATransform3D t) :獲取t變換矩陣所包含的CGAffineTransform變換矩陣。

CAPropertyAnimation使用
1:利用+ (instancetype)animationWithKeyPath:(NSString *)keyPath類方法創(chuàng)建屬性動(dòng)畫
2:如果使用基本屬性動(dòng)畫CABasicAnimation,則可指定fromValue,toValue兩個(gè)屬性值,其中fromValue指定動(dòng)畫屬性開始時(shí)的屬性值,toValue指定動(dòng)畫屬性結(jié)束的屬性值.如果使用CAKeyframeAnimation屬性動(dòng)畫,則指定values屬性值,該屬性值是一個(gè) NSArray屬性,其中第一個(gè)元素是屬性的開始值.依次變化.區(qū)別在于CABasicAnimation只能夠指定開始值和結(jié)束值.而CAKeyframeAnimation則可以指定動(dòng)畫屬性的多個(gè)值.CAKeyframeAnimation還可以通過控制路勁來控制移動(dòng).
3:調(diào)用CALayer- (void)addAnimation:(CAAnimation *)anim forKey:(NSString *)key添加動(dòng)畫即可.key用于多圖層進(jìn)行分辨.單圖層看不用設(shè)置.

舉例:demo 實(shí)現(xiàn)基本的位移 縮放 旋轉(zhuǎn) 組合動(dòng)畫

#import <QuartzCore/QuartzCore.h>
#import "FKViewController.h"

@implementation FKViewController
CALayer *imageLayer;
- (void)viewDidLoad
{
[superviewDidLoad];
  
// 創(chuàng)建一個(gè)CALayer對(duì)象
imageLayer = [CALayerlayer];
//設(shè)置該CALayer的邊框、大小、位置等屬性
imageLayer.cornerRadius =6;
imageLayer.borderWidth =1;
imageLayer.borderColor = [UIColorblackColor].CGColor;
imageLayer.masksToBounds =YES;
imageLayer.frame =CGRectMake(30,30, 100, 135);
// 設(shè)置該imageLayer顯示的圖片
imageLayer.contents = (id)[[UIImageimageNamed:@"android"]CGImage];
[self.view.layeraddSublayer:imageLayer];
    
NSArray* bnTitleArray = [NSArrayarrayWithObjects:@"位移"
, @"旋轉(zhuǎn)" ,@"縮放" ,@"動(dòng)畫組" ,nil];
//獲取屏幕的內(nèi)部高度
CGFloat totalHeight = [UIScreenmainScreen].bounds.size.height;
NSMutableArray* bnArray = [[NSMutableArrayalloc] init];
//采用循環(huán)創(chuàng)建4個(gè)按鈕
for(int i =0 ; i < 4 ; I++)
{
UIButton* bn = [UIButtonbuttonWithType:UIButtonTypeRoundedRect];
bn.frame =CGRectMake(5 + i *80, totalHeight - 45 - 20 , 70 ,35);
[bnsetTitle:[bnTitleArray objectAtIndex:i]
forState:UIControlStateNormal];
[bnArrayaddObject:bn];
[self.viewaddSubview:bn];
}
//為4個(gè)按鈕綁定不同的事件處理方法
[[bnArray objectAtIndex:0]addTarget:selfaction:@selector(move:)
forControlEvents:UIControlEventTouchUpInside];
[[bnArray objectAtIndex:1]addTarget:selfaction:@selector(rotate:)
forControlEvents:UIControlEventTouchUpInside];
[[bnArray objectAtIndex:2]addTarget:selfaction:@selector(scale:)
forControlEvents:UIControlEventTouchUpInside];
[[bnArray objectAtIndex:3]addTarget:selfaction:@selector(group:)
forControlEvents:UIControlEventTouchUpInside];
}
-(void) move:(id)sender
{
CGPoint fromPoint =imageLayer.position;
CGPoint toPoint =CGPointMake(fromPoint.x +80 , fromPoint.y);
// 創(chuàng)建不斷改變CALayer的position屬性的屬性動(dòng)畫
CABasicAnimation* anim = [CABasicAnimation
animationWithKeyPath:@"position"];
//設(shè)置動(dòng)畫開始的屬性值
anim.fromValue = [NSValuevalueWithCGPoint:fromPoint];
//設(shè)置動(dòng)畫結(jié)束的屬性值
anim.toValue = [NSValuevalueWithCGPoint:toPoint];
anim.duration =0.5;
imageLayer.position = toPoint;//設(shè)置移動(dòng)后圖片的位置。
anim.removedOnCompletion =YES;
// 為imageLayer添加動(dòng)畫
[imageLayeraddAnimation:anim forKey:nil];
}
-(void) rotate:(id)sender
{
// 創(chuàng)建不斷改變CALayer的transform屬性的屬性動(dòng)畫
CABasicAnimation* anim = [CABasicAnimationanimationWithKeyPath:@"transform"];
CATransform3D fromValue =imageLayer.transform;
//設(shè)置動(dòng)畫開始的屬性值
anim.fromValue = [NSValuevalueWithCATransform3D:fromValue];
// 繞X軸旋轉(zhuǎn)180度
CATransform3D toValue =CATransform3DRotate(fromValue, M_PI , 1 , 0 , 0);
// 繞Y軸旋轉(zhuǎn)180度
// CATransform3D toValue = CATransform3DRotate(fromValue, M_PI , 0 , 1 , 0);
// // 繞Z軸旋轉(zhuǎn)180度
// CATransform3D toValue = CATransform3DRotate(fromValue, M_PI , 0 , 0 , 1);
//設(shè)置動(dòng)畫結(jié)束的屬性值
anim.toValue = [NSValuevalueWithCATransform3D:toValue];
anim.duration =0.5;
imageLayer.transform = toValue;
anim.removedOnCompletion =YES;
// 為imageLayer添加動(dòng)畫
[imageLayeraddAnimation:anim forKey:nil];
}
-(void) scale:(id)sender
{
// 創(chuàng)建不斷改變CALayer的transform屬性的屬性動(dòng)畫
CAKeyframeAnimation* anim = [CAKeyframeAnimation
animationWithKeyPath:@"transform"];
// 設(shè)置CAKeyframeAnimation控制transform屬性依次經(jīng)過的屬性值
anim.values = [NSArrayarrayWithObjects:
[NSValuevalueWithCATransform3D:imageLayer.transform],
[NSValuevalueWithCATransform3D:CATransform3DScale
(imageLayer.transform ,0.2, 0.2, 1)],
[NSValuevalueWithCATransform3D:CATransform3DScale
(imageLayer.transform,2, 2 , 1)],
[NSValuevalueWithCATransform3D:imageLayer.transform],nil];
anim.duration =5;
anim.removedOnCompletion =YES;
// 為imageLayer添加動(dòng)畫
[imageLayeraddAnimation:anim forKey:nil];
}
-(void) group:(id)sender
{
CGPoint fromPoint =imageLayer.position;
CGPoint toPoint =CGPointMake(280 , fromPoint.y +300);
// 創(chuàng)建不斷改變CALayer的position屬性的屬性動(dòng)畫
CABasicAnimation* moveAnim = [CABasicAnimation
animationWithKeyPath:@"position"];
//設(shè)置動(dòng)畫開始的屬性值 
moveAnim.fromValue = [NSValuevalueWithCGPoint:fromPoint];
//設(shè)置動(dòng)畫結(jié)束的屬性值 
moveAnim.toValue = [NSValuevalueWithCGPoint:toPoint];
moveAnim.removedOnCompletion =YES;
// 創(chuàng)建不斷改變CALayer的transform屬性的屬性動(dòng)畫
CABasicAnimation* transformAnim = [CABasicAnimation
animationWithKeyPath:@"transform"];
CATransform3D fromValue =imageLayer.transform;
//設(shè)置動(dòng)畫開始的屬性值
transformAnim.fromValue = [NSValuevalueWithCATransform3D: fromValue];
//創(chuàng)建縮放為X、Y兩個(gè)方向上縮放為0.5的變換矩陣
CATransform3D scaleValue =CATransform3DScale(fromValue , 0.5 , 0.5, 1);
//繞Z軸旋轉(zhuǎn)180度的變換矩陣
CATransform3D rotateValue =CATransform3DRotate(fromValue, M_PI , 0 , 0 , 1);
//計(jì)算兩個(gè)變換矩陣的和
CATransform3D toValue =CATransform3DConcat(scaleValue, rotateValue);
//設(shè)置動(dòng)畫技術(shù)的屬性值 
transformAnim.toValue = [NSValuevalueWithCATransform3D:toValue];
//動(dòng)畫效果累加
transformAnim.cumulative =YES;
//動(dòng)畫重復(fù)執(zhí)行2次,旋轉(zhuǎn)360度
transformAnim.repeatCount =2;
transformAnim.duration =3;
//位移、縮放、旋轉(zhuǎn)組合起來執(zhí)行
CAAnimationGroup *animGroup = [CAAnimationGroupanimation];
animGroup.animations = [NSArrayarrayWithObjects:moveAnim
, transformAnim ,nil];
animGroup.duration =6;
// 為imageLayer添加動(dòng)畫
[imageLayeraddAnimation:animGroup forKey:nil];
}
@end

舉例:控制移動(dòng)的路徑
對(duì)于CAKeyframeAnimation而言,它除了可通過values屬性指定動(dòng)畫過程中的多個(gè)值之外,還可以通過path屬性指定CALayer的移動(dòng)路徑,改屬性就是CGPathRef,通過這種方式即可控制CALayer按我們指定的軌跡移動(dòng),從而執(zhí)行更加細(xì)致的動(dòng)畫。

#import <QuartzCore/QuartzCore.h>
#import "FKViewController.h"

@interface FKViewController ()

@end

@implementation FKViewController
CALayer* fishLayer;
NSInteger fishFrame;
NSTimer* timer;
// 定義NSMutableArray裝魚的10個(gè)動(dòng)畫幀
NSMutableArray* fishFrameArray;
- (void)viewDidLoad
{
[superviewDidLoad];
    
// 創(chuàng)建CALayer作為背景
CALayer* bg = [CALayerlayer];
//設(shè)置背景圖片
bg.contents = (id)[UIImageimageNamed:@"bg.jpg"].CGImage;
bg.contentsGravity =kCAGravityCenter;
bg.frame =CGRectMake(0,0, 320, 480);
[self.view.layeraddSublayer:bg];
fishFrameArray = [[NSMutableArrayalloc] init];
// 初始化魚的10個(gè)動(dòng)畫幀,并添加到fishFrameArray集合中
for(int i =0 ; i < 10 ; I++)
{
[fishFrameArrayaddObject:[UIImageimageNamed:
[NSStringstringWithFormat:@"fish%d.png" , i]]];//多張連串的圖片
}
//創(chuàng)建定時(shí)器控制小魚的動(dòng)畫幀的改變。
timer = [NSTimerscheduledTimerWithTimeInterval:0.15target:self
selector:@selector(change)userInfo:nilrepeats:YES];
// 創(chuàng)建CALayer
fishLayer = [CALayerlayer];
//設(shè)置CALayer顯示內(nèi)容的對(duì)齊、縮放模式(不縮放,直接顯示在中間)
fishLayer.contentsGravity =kCAGravityCenter;
// 設(shè)置fishLayer的大小
fishLayer.frame =CGRectMake(128,6, 90, 40);
[self.view.layeraddSublayer:fishLayer];
    
//創(chuàng)建一個(gè)按鈕,通過該按鈕觸發(fā)小魚的游動(dòng)
UIButton* bn = [UIButtonbuttonWithType:UIButtonTypeRoundedRect];
bn.frame =CGRectMake(10 ,10 , 60 , 35);
[bn setTitle:@"開始"forState:UIControlStateNormal];
[self.viewaddSubview:bn];
//用戶點(diǎn)擊按鈕時(shí),激發(fā)start:方法
[bnaddTarget:selfaction:@selector(start:)
  forControlEvents:UIControlEventTouchUpInside];
}
-(void) start:(id)sender
{
// 創(chuàng)建對(duì)CALayer的position屬性執(zhí)行控制的屬性動(dòng)畫
CAKeyframeAnimation* anim = [CAKeyframeAnimation
animationWithKeyPath:@"position"];
// 創(chuàng)建路徑
CGMutablePathRef movePath =CGPathCreateMutable();
//添加一條圓形的路徑
CGPathAddArc(movePath,nil, 170, 175, 150, -M_PI /2, M_PI * 3 / 2, YES);
//設(shè)置anim動(dòng)畫的移動(dòng)路徑
anim.path = movePath;
    
// 創(chuàng)建對(duì)CALayer的transform屬性執(zhí)行控制的屬性動(dòng)畫
CAKeyframeAnimation* anim2 = [CAKeyframeAnimation
animationWithKeyPath:@"transform"];
//指定關(guān)鍵幀動(dòng)畫的3個(gè)關(guān)鍵值:分別是不旋轉(zhuǎn),旋轉(zhuǎn)180度,旋轉(zhuǎn)360度
anim2.values = [NSArrayarrayWithObjects:
[NSValuevalueWithCATransform3D:CATransform3DIdentity],
[NSValuevalueWithCATransform3D:
CATransform3DMakeRotation(M_PI , 0, 0,1)],
[NSValuevalueWithCATransform3D:
CATransform3DMakeRotation(2 * M_PI , 0,0, 1)]
,nil];
    
//使用動(dòng)畫組來組合2個(gè)動(dòng)畫
CAAnimationGroup *animGroup = [CAAnimationGroupanimation];
animGroup.animations = [NSArrayarrayWithObjects:anim, anim2, nil];
//指定動(dòng)畫重復(fù)10次
animGroup.repeatCount =10;
animGroup.duration =24;
// 為fishLayer添加動(dòng)畫
[fishLayeraddAnimation:animGroup forKey:@"move"];
}
// 該方法由定時(shí)器觸發(fā),不斷更改fishLayer顯示的動(dòng)畫幀
- (void) change
{
fishLayer.contents = (id)[[fishFrameArray
objectAtIndex:fishFrame++ %10] CGImage];
}
@end
4:CAAnimationGroup 動(dòng)畫組,方便對(duì)于多動(dòng)畫的統(tǒng)一控制管理。

能將多個(gè)動(dòng)畫組合在一起,如平移、縮放、旋轉(zhuǎn)等效果組合在一起做出更炫酷的的效果。它只有一個(gè)特有的屬性。

  • animations-> 所有動(dòng)畫效果元素的數(shù)組。

舉例1:

@property(nullable, copy) NSArray<CAAnimation *> *animations;
NSLog(@"動(dòng)畫執(zhí)行前redView.frame:%@", NSStringFromCGRect(self.redView.frame));

 //平移
CABasicAnimation *transition = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
transition.toValue = @(300);

//旋轉(zhuǎn)
CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotation.toValue = @(M_PI);

//縮放
CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scale.toValue = @(0.2);

//添加組動(dòng)畫
CAAnimationGroup *group = [CAAnimationGroup animation];

//注意這里動(dòng)畫的效果 要設(shè)置成group的
group.duration = 2.0;
group.animations = @[rotation, scale, transition];
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;

[self.redView.layer addAnimation:group forKey:nil];
image.gif

舉例2:

@interface TestAnimationGroupVC ()

@property (nonatomic,strong) UIView *colorView;
@property (nonatomic,strong) UIBezierPath *bezierPath;

@end

@implementation TestAnimationGroupVC

- (void)viewDidLoad {
    [super viewDidLoad];
    //創(chuàng)建顯示顏色的圖層
    self.colorView = [UIView new];
    self.colorView.frame = CGRectMake(0, 0, 60, 60);
    self.colorView.center = CGPointMake(50, 200);
    self.colorView.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:self.colorView];
    
    //創(chuàng)建貝塞爾曲線,即幀動(dòng)畫運(yùn)動(dòng)軌跡
    self.bezierPath  = [[UIBezierPath alloc] init];
    [self.bezierPath moveToPoint:CGPointMake(50, 200)];
    [self.bezierPath addCurveToPoint:CGPointMake(kDeviceWidth - 50, 200) controlPoint1:CGPointMake(150, 50) controlPoint2:CGPointMake(kDeviceWidth - 150, 250)];
    
    //繪制繪制path,便于觀察動(dòng)畫;
    CAShapeLayer *pathLayer = [CAShapeLayer layer];
    pathLayer.path = self.bezierPath.CGPath;
    pathLayer.fillColor = [UIColor clearColor].CGColor;
    pathLayer.strokeColor = [UIColor redColor].CGColor;
    pathLayer.lineWidth = 3.0f;
    [self.view.layer addSublayer:pathLayer];
}

- (IBAction)startAnimation:(UIButton *)sender{   
    //移除可能未執(zhí)行完的動(dòng)畫,防止多重動(dòng)畫導(dǎo)致異常
    [self.colorView.layer removeAnimationForKey:@"groupAnimation"];
    
    //1.創(chuàng)建基礎(chǔ)動(dòng)畫:修改背景色為紫色
    CABasicAnimation *basicAnimation = [CABasicAnimation animation];
    basicAnimation.keyPath = @"backgroundColor";
    basicAnimation.toValue = (__bridge id _Nullable)([UIColor purpleColor].CGColor);
    
    //2.創(chuàng)建關(guān)鍵幀動(dòng)畫
    CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animation];
    keyFrameAnimation.keyPath = @"position";
    keyFrameAnimation.path = self.bezierPath.CGPath;
    keyFrameAnimation.rotationMode = kCAAnimationRotateAuto;
    
    //3.創(chuàng)建組動(dòng)畫:組合基礎(chǔ)動(dòng)畫和關(guān)鍵幀動(dòng)畫
    CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
    groupAnimation.animations = @[basicAnimation, keyFrameAnimation];
    groupAnimation.duration = 4.0;
    [self.colorView.layer addAnimation:groupAnimation forKey:@"groupAnimation"];
}

動(dòng)畫組的效果如下:


image.gif
5:CABasicAnimation基礎(chǔ)動(dòng)畫

CABasicAnimation即基礎(chǔ)動(dòng)畫,在指定可動(dòng)畫屬性后,動(dòng)畫會(huì)按照預(yù)定的參數(shù)持續(xù)一定時(shí)間由初始值變換為終點(diǎn)值。其實(shí),CABasicAnimation就相當(dāng)于只有開始和結(jié)束兩個(gè)幀的特殊關(guān)鍵幀動(dòng)畫(后續(xù)會(huì)詳解);

屬性 屬性說明

  • fromValue-> 起始值
  • toValue-> 結(jié)束值
  • byValue-> keyPath屬性的變化值

下面的示例使用CABasicAnimation實(shí)現(xiàn)了修改顏色圖層colorLayer的背景色為隨機(jī)顏色的動(dòng)畫,具體的代碼如下:

@interface TestBacicAnimation1VC ()<CAAnimationDelegate>
@property (nonatomic,strong) CALayer *colorLayer;
@end

@implementation TestBacicAnimation1VC

- (void)viewDidLoad {
    [super viewDidLoad];
    //創(chuàng)建顯示顏色的圖層,添加于視圖控制器的View上
    CALayer *colorLayer = [CALayer layer];
    colorLayer.frame = CGRectMake(50, 50, 100, 100);
    colorLayer.backgroundColor = [UIColor redColor].CGColor;
    self.colorLayer = colorLayer;
    [self.view.layer addSublayer:colorLayer];
}

- (IBAction)changeColor:(UIButton *)sender{
    //步驟1:創(chuàng)建動(dòng)畫
    CABasicAnimation *animation = [CABasicAnimation animation];
    animation.keyPath = @"backgroundColor";
    //步驟2:設(shè)定動(dòng)畫屬性
    animation.autoreverses = NO;
    animation.duration = 0.25;
    animation.repeatCount = 1;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    animation.delegate = self;
    UIColor *randomColor = [UIColor randomColor];  //自定義獲取隨機(jī)色的方法
    animation.toValue = (__bridge id _Nullable)(randomColor.CGColor);
    //步驟3:添加動(dòng)畫到圖層
    [self.colorLayer addAnimation:animation forKey:@"keyPath_backgroundColor"];
}

- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag{
    //禁用隱式動(dòng)畫
    [CATransaction begin];
    [CATransaction setDisableActions:true];
    self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;
    [CATransaction commit];
}

效果如下:

image.gif

總結(jié)創(chuàng)建動(dòng)畫的兩種方式如下:

//方法1:實(shí)例化同時(shí)指定動(dòng)畫類型
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];

//方法2:先實(shí)例化,再指定動(dòng)畫類型
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"backgroundColor";

關(guān)閉隱式動(dòng)畫

對(duì)獨(dú)立圖層(即非UIView的關(guān)聯(lián)圖層,類似上述例子中的colorLayer)做更新屬性的顯式動(dòng)畫,我們需要設(shè)置一個(gè)事務(wù)來禁用圖層行為,否則動(dòng)畫會(huì)發(fā)生兩次,一次是因?yàn)轱@式的CABasicAnimation,另一次是因?yàn)殡[式動(dòng)畫,從而導(dǎo)致我們看到的動(dòng)畫異常。

6:CAKeyframeAnimation 關(guān)鍵幀動(dòng)畫

CAKeyframeAnimation 關(guān)鍵幀動(dòng)畫,同樣通過·keyPath·對(duì)應(yīng)屬性進(jìn)行控制,但它可以通過·values·或者·path·進(jìn)行多個(gè)階段的控制,屬性如下:

  • values -> 關(guān)鍵幀組成的數(shù)組,動(dòng)畫會(huì)依次顯示其中的每一幀。
  • path-> 關(guān)鍵幀路徑,動(dòng)畫進(jìn)行的要素,優(yōu)先級(jí)比values高,但是只對(duì)CALayer的anchorPoint和position起作用。如果你設(shè)置了path,那么values將被忽略
  • keyTimes -> 每一幀對(duì)應(yīng)的時(shí)間,如果不設(shè)置,則各關(guān)鍵幀平分設(shè)定時(shí)間。
  • timingFunctions -> 每一幀對(duì)應(yīng)的動(dòng)畫節(jié)奏。
  • calculationMode-> 動(dòng)畫的計(jì)算模式,系統(tǒng)提供了對(duì)應(yīng)的幾種模式。
  • tensionValues -> 動(dòng)畫張力控制。
  • continuityValues-> 動(dòng)畫連續(xù)性控制。
  • biasValues -> 動(dòng)畫偏差率控制。
  • rotationMode-> 動(dòng)畫沿路徑旋轉(zhuǎn)方式,系統(tǒng)提供了兩種模式。

簡(jiǎn)單的創(chuàng)建一個(gè)帶路徑的動(dòng)畫效果,比較粗糙,不過事先原理都是一樣的
代碼如下:
實(shí)現(xiàn)幀動(dòng)畫:使用values

從關(guān)鍵幀動(dòng)畫的屬性可以看出,我們可以總結(jié)出關(guān)鍵幀動(dòng)畫的實(shí)現(xiàn)方式實(shí)際分為兩種:
1.通過values設(shè)置關(guān)鍵幀屬性值數(shù)組;
2.通過path設(shè)置關(guān)鍵幀路徑,而且此種方式的優(yōu)先級(jí)較高;
這里首先測(cè)試第一種方式,實(shí)現(xiàn)這樣的關(guān)鍵幀動(dòng)畫:創(chuàng)建一個(gè)紫色滑塊在四個(gè)坐標(biāo)點(diǎn)之間滑動(dòng);具體的代碼實(shí)現(xiàn)如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    //創(chuàng)建測(cè)試幀動(dòng)畫的紫色圖層
    UIView *purpleView = [UIView new];
    purpleView.frame = CGRectMake(0, 0, 50, 50);
    purpleView.center = CGPointMake(50, 100);
    purpleView.backgroundColor = [UIColor purpleColor];
    [self.view addSubview:purpleView];
    
    //步驟1:創(chuàng)建動(dòng)畫
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    //步驟2:設(shè)置動(dòng)畫關(guān)鍵幀數(shù)據(jù)
    NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(50, 100)];
    NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(kDeviceWidth -50, 100)];
    NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(kDeviceWidth -50, kDeviceWidth- 100)];
    NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(50, kDeviceWidth -100)];
    NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(50, 100)];
    animation.values = @[value1,value2,value3,value4,value5];
    //步驟3:設(shè)定動(dòng)畫屬性
    animation.repeatCount = MAXFLOAT; //重復(fù)執(zhí)行
    animation.autoreverses = NO;
    animation.removedOnCompletion = NO;
    animation.duration = 4;
    //animation.keyTimes = @[@(0), @(1 / 10.0), @(5 / 10.0), @(9 / 10.0), @(1) ];
    animation.timingFunctions  = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],
                                   [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                   [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
                                   [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];

    [purpleView.layer addAnimation:animation forKey:nil];
}

關(guān)鍵幀動(dòng)畫效果如下:


image.gif

實(shí)現(xiàn)關(guān)鍵幀動(dòng)畫:使用path

現(xiàn)在,我們測(cè)試CAKeyframeAnimation使用path實(shí)現(xiàn)這樣一個(gè)動(dòng)畫:一架飛機(jī)沿著一個(gè)簡(jiǎn)單的曲線運(yùn)動(dòng)飛行;具體的操作包括以下幾個(gè)步驟:
1.使用UIKit提供的UIBezierPath類創(chuàng)建貝塞爾曲線,作為飛機(jī)飛行的路線軌跡;
2.使用CAShapeLayer在屏幕上繪制曲線(此步驟對(duì)于動(dòng)畫不是必須的,只是為了動(dòng)畫看起來更直觀);
3.創(chuàng)建用于顯示飛機(jī)的視圖,將其設(shè)置在貝塞爾曲線的初始位置;
4.創(chuàng)建并執(zhí)行關(guān)鍵幀動(dòng)畫,實(shí)現(xiàn)飛機(jī)飛行的曲線動(dòng)畫;

- (void)viewDidLoad {
    [super viewDidLoad];
    //1.創(chuàng)建三次貝塞爾曲線(一種使用起始點(diǎn),結(jié)束點(diǎn)和另外兩個(gè)控制點(diǎn)定義的曲線);
    UIBezierPath *bezierPath  = [[UIBezierPath alloc] init];
    [bezierPath moveToPoint:CGPointMake(50, 200)];
    [bezierPath addCurveToPoint:CGPointMake(kDeviceWidth - 50, 200) controlPoint1:CGPointMake(150, 50) controlPoint2:CGPointMake(kDeviceWidth - 150, 250)];
    
    //2.繪制飛行路線
    CAShapeLayer *pathLayer = [CAShapeLayer layer];
    pathLayer.path = bezierPath.CGPath;
    pathLayer.fillColor = [UIColor clearColor].CGColor;
    pathLayer.strokeColor = [UIColor redColor].CGColor;
    pathLayer.lineWidth = 3.0f;
    [self.view.layer addSublayer:pathLayer];
    
    //3.創(chuàng)建顯示飛機(jī)的視圖
    UIImageView *airPlaneImgView = [[UIImageView alloc] init];
    airPlaneImgView.frame = CGRectMake(0, 0, 50, 50);
    airPlaneImgView.center = CGPointMake(50, 200);
    airPlaneImgView.image = [UIImage imageNamed:@"airplane"];
    [self.view addSubview:airPlaneImgView];
    
    //4.設(shè)置關(guān)鍵幀動(dòng)畫
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 5.0;
    animation.path = bezierPath.CGPath;
    animation.rotationMode = kCAAnimationRotateAuto; //設(shè)置根據(jù)曲線的切線自動(dòng)旋轉(zhuǎn),讓動(dòng)畫更加真實(shí)
    [airPlaneImgView.layer addAnimation:animation forKey:nil];
}

關(guān)鍵幀動(dòng)畫效果圖如下:


image.gif
7:CATransition 轉(zhuǎn)場(chǎng)動(dòng)畫,系統(tǒng)提供了很多酷炫效果。屬性如下:
  • type -> 轉(zhuǎn)場(chǎng)動(dòng)畫類型。
    kCATransitionFade:交叉淡化過渡
    kCATransitionPush:新視圖吧舊視圖
    kCATransitionMoveIn:新視圖移到舊視圖上面
    kCATransitionReveal:將舊視圖移開,顯示下面的視圖
    用字符串表示:
    pageCurl:向上翻頁效果
    pageUnCurl:向下翻頁效果
    cube:立方體翻滾效果
    oglFlip:上下左右翻轉(zhuǎn)效果
    rippleEffect:波紋
    suckEffect:吮吸
    flipFromLeft:左翻轉(zhuǎn)
    flipFromRight:右翻轉(zhuǎn)
    suckEffect:收縮效果,如一塊布被抽走
    rippleEffect:水滴效果
    cameraIrisHollowOpen:相機(jī)鏡頭打開效果
    cameraIrisHollowClose:相機(jī)鏡頭關(guān)閉效果
  • subtype -> 轉(zhuǎn)場(chǎng)動(dòng)畫方向。
    kCATransitionFromRight 從右向左,kCATransitionFromTop 從上向下,kCATransitionFromLeft 從左向右,kCATransitionFromBottom 從下向上
  • startProgress-> 動(dòng)畫起點(diǎn)進(jìn)度(整體的百分比)。
  • endProgress-> 動(dòng)畫終點(diǎn)進(jìn)度(整體的百分比)。
  • filter -> 自定義轉(zhuǎn)場(chǎng)。
  • duration:動(dòng)畫執(zhí)行時(shí)間
  • timingFunction: 動(dòng)畫的運(yùn)動(dòng)軌跡,用于變化起點(diǎn)和終點(diǎn)之間的插值計(jì)算,形象點(diǎn)說它決定了動(dòng)畫運(yùn)行的節(jié)奏,比如是均勻變化(相同時(shí)間變化量相同)還是先快后慢,先慢后快還是先慢再快再慢
    kCAMediaTimingFunctionLinear 線性,即勻速
    kCAMediaTimingFunctionEaseIn 先慢后快
    kCAMediaTimingFunctionEaseOut 先快后慢
    kCAMediaTimingFunctionEaseInEaseOut 先慢后快再慢
    kCAMediaTimingFunctionDefault 實(shí)際效果是動(dòng)畫中間比較快

示例1:

//轉(zhuǎn)場(chǎng)動(dòng)畫
-(void)transitionAnimation{
    CATransition *transtion = [CATransition animation];
    transtion.type = @"rippleEffect";
    transtion.subtype = kCATransitionFromLeft;//kCATransitionFromLeft  kCATransitionFromRight
    transtion.duration = 1;
    _transtionIndex++;
    if (_transtionIndex > 4) {
        _transtionIndex = 1;
    }
    _aniLayer.contents = (id)[UIImage imageNamed:[NSString stringWithFormat:@"%@.jpg",@(_transtionIndex)]].CGImage;
    [_aniLayer addAnimation:transtion forKey:@"transtion"];
}

示例2:

//創(chuàng)建動(dòng)畫
    CATransition *transition         = [[CATransition alloc]init];
    transition.duration              = 0.5;
    transition.timingFunction        = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    transition.type                  = kCATransitionReveal;
    transition.subtype               = kCATransitionFromBottom;
    UULoginUserViewController *login = [[UULoginUserViewController alloc] init];
    UUNavigationController *nav      = [[UUNavigationController alloc] initWithRootViewController:login];
    self.window.rootViewController   = nav;
    [self.window.layer addAnimation:transition forKey:@"animation"];

示例3:

@interface TestTransition1VC ()

@property (nonatomic,strong) UIImageView *imageView;
@property (nonatomic,strong) NSArray *images;

@property (nonatomic, copy) NSString *type;
@property (nonatomic, copy) NSString *subtype;

@end

@implementation TestTransition1VC

- (void)viewDidLoad {
    [super viewDidLoad];
    self.images = @[[UIImage imageNamed:@"tree_spring"],
                    [UIImage imageNamed:@"tree_summer"],
                    [UIImage imageNamed:@"tree_autumn"],
                    [UIImage imageNamed:@"tree_winter"]];
    
    self.type = kCATransitionFade;
    self.subtype = kCATransitionFromRight;
}

- (void)perforomTransitionAnimation{
    CATransition *transition = [[CATransition alloc] init];
    transition.type = _type;
    transition.subtype = _subtype;
    transition.duration = 0.5;
    [self.imageView.layer addAnimation:transition forKey:nil];
    
    UIImage *currentImage = self.imageView.image;
    NSUInteger index = [self.images indexOfObject:currentImage];
    index = (index + 1) % self.images.count;
    self.imageView.image = self.images[index];
}

過渡動(dòng)畫的效果如下:


image.gif

自定義過渡動(dòng)畫

過渡動(dòng)畫的過程就是對(duì)原始圖層外觀截圖,然后添加一段動(dòng)畫,平滑過渡到圖層改變之后的那個(gè)截圖效果。如果我們知道如何對(duì)圖層截圖,我們就可以使用屬性動(dòng)畫來自定義CATransition動(dòng)畫了。

CALayer有一個(gè)-renderInContenxt:方法,通過它可以將圖層繪制到Core Graphics的上下文中捕獲當(dāng)前內(nèi)容的圖片;所以現(xiàn)在我們嘗試這樣的實(shí)現(xiàn):對(duì)當(dāng)前視圖控制器View進(jìn)行截圖,然后在改變其背景色的時(shí)候?qū)貓D快速旋轉(zhuǎn)并且淡出,以達(dá)到一種過渡的效果;具體的代碼示例如下:

- (void)performAnimation{
    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();
    UIView *coverView = [[UIImageView alloc] initWithImage:coverImage];
    coverView.frame = self.view.bounds;
    [self.view addSubview:coverView];
    
    //使用自定義方法得到隨機(jī)顏色(切換后的顏色)
    UIColor *randomColor = [UIColor randomColor];
    self.view.backgroundColor = randomColor;
    
    //使用UIView動(dòng)畫方法來代替屬性動(dòng)畫(為了簡(jiǎn)化代碼步驟)
    [UIView animateWithDuration:1 animations:^{
        CGAffineTransform transform = CGAffineTransformMakeScale(0.01, 0.01);
        transform = CGAffineTransformRotate(transform, M_PI_2);
        coverView.transform = transform;
        coverView.alpha = 0.0;
    } completion:^(BOOL finished) {
        [coverView removeFromSuperview];
    }];
 }

自定義過渡動(dòng)畫的效果如下:


image.gif
7:CASpringAnimation彈簧動(dòng)畫

CASpringAnimation彈簧動(dòng)畫,帶有初始速度以及阻尼指數(shù)等物理參數(shù)的屬性動(dòng)畫。我們可以把它看成在不絕對(duì)光滑的地面上,一個(gè)彈簧拴著別小球,那么我們可以這么理解他的屬性(物理知識(shí)請(qǐng)問一下牛頓大叔):

  • mass -> 質(zhì)量越大,彈簧拉伸和壓縮的幅度越大,動(dòng)畫的速度變慢,并且波動(dòng)幅度變大,影響圖層運(yùn)動(dòng)時(shí)的彈簧慣性
  • stiffness-> 彈簧的勁度系數(shù)。剛度系數(shù)越大,形變產(chǎn)生的力就越大,運(yùn)動(dòng)越快
  • damping -> 阻尼系數(shù),地面的摩擦力。阻尼系數(shù)越大,停止越快
  • initialVelocity -> 初始速度,相當(dāng)于給小球一個(gè)初始速度(可正可負(fù),方向不同),初始速率,動(dòng)畫視圖的初始速度大小
    速率為正數(shù)時(shí),速度方向與運(yùn)動(dòng)方向一致,速率為負(fù)數(shù)時(shí),速度方向與運(yùn)動(dòng)方向相反
  • settlingDuration -> 結(jié)算時(shí)間 返回彈簧動(dòng)畫到停止時(shí)的估算時(shí)間,根據(jù)當(dāng)前的動(dòng)畫參數(shù)估算 通常彈簧動(dòng)畫的時(shí)間使用結(jié)算時(shí)間比較準(zhǔn)確

示例:

    self.animationView = [[UIView alloc]init];
    self.animationView.frame = CGRectMake(0, kHeight/2-50, 50, 50);
    self.animationView.backgroundColor = [UIColor greenColor];
    [self.view addSubview:self.animationView];
    
    //彈簧效果
    CASpringAnimation *spring  = [CASpringAnimation animationWithKeyPath:@"position.y"];
    spring.fromValue = @150;
    spring.toValue = @100;
    //阻尼系數(shù)
    spring.damping = 0.1;
    //剛度系數(shù): (勁度系數(shù) / 彈性系數(shù)): 系數(shù)越大,形變的產(chǎn)生的力越大, 運(yùn)動(dòng)越快
    spring.stiffness = 10;
    //質(zhì)量: 影響圖層運(yùn)動(dòng)時(shí)候的慣性, 質(zhì)量越大彈簧拉伸和壓縮的幅度越大 (動(dòng)畫的幅度,波動(dòng)變大)
    spring.mass = 1;
    //初識(shí)速率: 動(dòng)畫視圖的初識(shí)速度大小
    //速率為正時(shí)候, 速度方向與運(yùn)動(dòng)方向一致, 否則相仿.
    spring.initialVelocity = 1;
    // settlingDuration 結(jié)算時(shí)間,預(yù)估彈簧動(dòng)畫到停住的時(shí)間的估算, 根據(jù)當(dāng)前動(dòng)畫的各個(gè)參數(shù)估算, 通常彈簧動(dòng)畫的估算時(shí)間使用結(jié)算時(shí)間比較準(zhǔn)確
    spring.duration = spring.settlingDuration;
    [self.animationView.layer addAnimation:spring forKey:@"springAnimation"];

最后

看完這些相信你對(duì) iOS 中的動(dòng)畫有了一個(gè)詳細(xì)的了解, 其實(shí)單個(gè)動(dòng)畫都是比較簡(jiǎn)單的, 而復(fù)雜的動(dòng)畫其實(shí)都是由一個(gè)個(gè)簡(jiǎn)單的動(dòng)畫組裝而成的,所以遇到比較難得動(dòng)畫需求, 我們只要充分組裝不同的動(dòng)畫,就能實(shí)現(xiàn)出滿意的效果.

好記性不如爛筆頭, 光說不練假把戲, 建議大家結(jié)合我的代碼, 自己邊看邊練習(xí), 這樣才能記得牢, 才能轉(zhuǎn)換成自己的知識(shí).
此處有個(gè)demo,可以去看看別人寫的東西。
github: https://github.com/YTiOSer/YTAnimation

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • iOS核心動(dòng)畫 核心動(dòng)畫框架 CoreAnimation框架是基于OpenGL與CoreGraphics圖像處理框...
    小白PK大牛閱讀 432評(píng)論 0 0
  • 文章目錄 iOS動(dòng)畫 UIView動(dòng)畫2.1 設(shè)置UIView動(dòng)畫的兩種語法形式2.2 設(shè)置屬性形變動(dòng)畫的兩種類型...
    luonaerduo閱讀 1,036評(píng)論 0 2
  • 最近買了幾本書,接下來會(huì)陸續(xù)寫寫筆記,把書中干貨分享出來 筆記主要總結(jié)了書中講了什么內(nèi)容,希望讀者讀完后遇到相應(yīng)的...
    搬磚人666閱讀 2,151評(píng)論 0 6
  • 基礎(chǔ) 核心動(dòng)畫是 iOS 和 MacOS 上的圖形渲染和動(dòng)畫基礎(chǔ)結(jié)構(gòu),用于為應(yīng)用的視圖和其他視覺元素設(shè)置動(dòng)畫。 核...
    davon閱讀 2,284評(píng)論 0 8
  • 本文將分為四個(gè)部分介紹核心動(dòng)畫: 第一部分將介紹核心動(dòng)畫的基本概念。 第二部分將介紹動(dòng)畫實(shí)現(xiàn)原理。 第三部分將介紹...
    曲年閱讀 3,347評(píng)論 1 9

友情鏈接更多精彩內(nèi)容