一、核心動畫概念 -導入QuartzCore.framework框架 1⃣ 開發步驟1.初始化一個動畫對象(CAAnimation)並且設置一些動畫相關屬性 2.CALayer中很多屬性都可以通過CAAnimation實現動畫效果,包括:opacity、position、transform、boun ...
1.將動畫的所有方法封裝到一個類裡面 MyCAHelper.h #import <Foundation/Foundation.h> #import <QuartzCore/QuartzCore.h> #define kCAHelperAlphaAnimation @"opacity"; // 淡入淡出動畫 #define kCAHelperScaleAnimation @"transform.scale"; // 比例縮放動畫 #define kCAHelperRotationAnimation @"transform.rotation"; // 旋轉動畫 #define kCAHelperPositionAnimation @"position"; // 平移位置動畫 @interface MyCAHelper : NSObject #pragma mark - 基本動畫統一調用方法 + (CABasicAnimation *)myBasicAnimationWithType:(NSString *)animationType duration:(CFTimeInterval)duration from:(NSValue *)from to:(NSValue *)to autoRevereses:(BOOL)autoRevereses; #pragma mark - 關鍵幀動畫方法 #pragma mark 搖晃動畫 + (CAKeyframeAnimation *)myKeyShakeAnimationWithDuration:(CFTimeInterval)duration angle:(CGFloat)angle repeatCount:(CGFloat)repeatCount; #pragma mark 貝塞爾路徑動畫 + (CAKeyframeAnimation *)myKeyPathAnimationWithDuration:(CFTimeInterval)duration path:(UIBezierPath *)path; #pragma mark 彈力模擬動畫 + (CAKeyframeAnimation *)myKeyBounceAnimationFrom:(CGPoint)from to:(CGPoint)to duration:(CFTimeInterval)duration; @end MyCAHelper.m #import "MyCAHelper.h" @implementation MyCAHelper #pragma mark - 基本動畫統一調用方法 + (CABasicAnimation *)myBasicAnimationWithType:(NSString *)animationType duration:(CFTimeInterval)duration from:(NSValue *)from to:(NSValue *)to autoRevereses:(BOOL)autoRevereses { // 1. 實例化一個CA動畫對象 CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:animationType]; // 2. 設置動畫屬性 [anim setDuration:duration]; [anim setFromValue:from]; [anim setToValue:to]; [anim setAutoreverses:autoRevereses]; return anim; } #pragma mark - 關鍵幀動畫方法 #pragma mark 搖晃動畫 + (CAKeyframeAnimation *)myKeyShakeAnimationWithDuration:(CFTimeInterval)duration angle:(CGFloat)angle repeatCount:(CGFloat)repeatCount { // 1. 初始化動畫對象實例 CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"]; // 2. 設置動畫屬性 [anim setDuration:duration]; [anim setValues:@[@(angle), @(-angle), @(angle)]]; [anim setRepeatCount:repeatCount]; return anim; } #pragma mark 貝塞爾路徑動畫 + (CAKeyframeAnimation *)myKeyPathAnimationWithDuration:(CFTimeInterval)duration path:(UIBezierPath *)path { // 1. 初始化動畫對象實例 CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; // 2. 設置動畫屬性 [anim setDuration:duration]; [anim setPath:path.CGPath]; return anim; } #pragma mark 彈力模擬動畫 + (CAKeyframeAnimation *)myKeyBounceAnimationFrom:(CGPoint)from to:(CGPoint)to duration:(CFTimeInterval)duration { // 是一個基於路徑的動畫 // 首先定義一個路徑,記錄彈力模擬的整個路徑 CGMutablePathRef path = CGPathCreateMutable(); // 彈力模擬路徑創建代碼 // 計算起始點與目標點之間的位置偏移量,這個偏移量的目的是為了能夠計算出小球第一次延伸的長度 CGFloat offsetX = from.x - to.x; CGFloat offsetY = from.y - to.y; // 1. 移動到起始點 CGPathMoveToPoint(path, NULL, from.x, from.y); // 2. 將目標點的坐標添加到路徑之中 CGPathAddLineToPoint(path, NULL, to.x, to.y); // 3. 設置小球的彈力因數 CGFloat offsetDivider = 4.0f; while (YES) { // 加延伸方向的路徑 CGPathAddLineToPoint(path, NULL, to.x + offsetX / offsetDivider, to.y + offsetY / offsetDivider); // 再次將目標點添加到路徑 CGPathAddLineToPoint(path, NULL, to.x, to.y); // 彈力因數遞增,保證越來越接近目標點 offsetDivider += 6.0f; // 當小球的當前位置距離目標點足夠小,我們退出迴圈 if ((abs(offsetX / offsetDivider) < 10.0f) && (abs(offsetY / offsetDivider) < 10.0f)) { break; } } CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; [anim setPath:path]; // 釋放路徑 CGPathRelease(path); [anim setDuration:duration]; return anim; } @end #import "ViewController.h" #import "MyCAHelper.h" @interface ViewController () { UIView *_demoView; CGPoint location; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self.view setBackgroundColor:[UIColor lightGrayColor]]; _demoView = [[UIView alloc]initWithFrame:CGRectMake(50, 50, 100, 100)]; [_demoView setBackgroundColor:[UIColor whiteColor]]; [self.view addSubview:_demoView]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; location = [touch locationInView:self.view]; // [_demoView setCenter:location]; /** 1.測試基本動畫 */ // CABasicAnimation *anim = [self testBasic1]; // [anim setRepeatCount:3]; // // [_demoView.layer addAnimation:anim forKey:nil]; /** 2.測試彈力模擬動畫效果 */ // [_demoView.layer addAnimation:[self test1:_demoView.center to:location] forKey:nil]; /** 3.測試路徑關鍵幀動畫 */ // [_demoView.layer addAnimation:[self test2] forKey:nil]; // [_demoView.layer addAnimation:[self test4:_demoView.center to:location] forKey:nil]; /** 4.測試搖晃關鍵幀動畫 */ // 點擊屏幕,開始搖晃,再次點擊,停止搖晃 // CAAnimation *anim = [_demoView.layer animationForKey:@"shakeAnimation"]; // if (anim) { // [_demoView.layer removeAnimationForKey:@"shakeAnimation"]; // } else { // [_demoView.layer addAnimation:[self test5] forKey:@"shakeAnimation"]; // } CAKeyframeAnimation *anim = [self test1:_demoView.center to:location]; [_demoView.layer addAnimation:anim forKey:nil]; } -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { // 需要在這裡對不同對象的動畫方法進行完成處理! [_demoView setCenter:location]; NSLog(@"%@", NSStringFromCGPoint(_demoView.center)); } #pragma mark - 重構方法測試 #pragma mark 測試貝塞爾路徑關鍵幀動畫 - (CAKeyframeAnimation *)test5 { return [MyCAHelper myKeyShakeAnimationWithDuration:0.2 angle:M_PI_4 / 18 repeatCount:MAXFLOAT]; } #pragma mark 測試貝塞爾路徑關鍵幀動畫 - (CAKeyframeAnimation *)test4:(CGPoint)from to:(CGPoint)to { UIBezierPath *path = [UIBezierPath bezierPath]; // 有兩個控制點去擠出的曲線,能擠出S型的曲線 [path moveToPoint:from]; [path addCurveToPoint:to controlPoint1:CGPointMake(320, 0) controlPoint2:CGPointMake(0, 460)]; return [MyCAHelper myKeyPathAnimationWithDuration:2.0 path:path]; } #pragma mark 測試貝塞爾路徑關鍵幀動畫 - (CAKeyframeAnimation *)test3:(CGPoint)from to:(CGPoint)to { UIBezierPath *path = [UIBezierPath bezierPath]; // 只有一個控制點去擠出的曲線 [path moveToPoint:from]; [path addQuadCurveToPoint:to controlPoint:CGPointMake(320, 230)]; return [MyCAHelper myKeyPathAnimationWithDuration:2.0 path:path]; } #pragma mark 測試路徑關鍵幀動畫 - (CAKeyframeAnimation *)test2 { UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(100, 100, 100, 100)]; return [MyCAHelper myKeyPathAnimationWithDuration:2.0 path:path]; } #pragma mark 測試彈力模擬動畫效果 - (CAKeyframeAnimation *)test1:(CGPoint)from to:(CGPoint)to { CAKeyframeAnimation *anim = [MyCAHelper myKeyBounceAnimationFrom:from to:to duration:1.5]; [anim setFillMode:kCAFillModeForwards]; [anim setRemovedOnCompletion:NO]; [anim setDelegate:self]; return anim; } - (CABasicAnimation *)testBasic1 { return [MyCAHelper myBasicAnimationWithType:@"opacity" duration:1.0 from:@(1.0) to:@(0.3) autoRevereses:YES]; } @end
一、核心動畫概念
-導入QuartzCore.framework框架
1⃣ 開發步驟
1.初始化一個動畫對象(CAAnimation)並且設置一些動畫相關屬性
2.CALayer中很多屬性都可以通過CAAnimation實現動畫效果,包括:opacity、position、transform、bounds、contents等(可以在API文檔中搜索:CALayer Animatable Properties)
3.添加動畫對象到層(CALayer)中,開始執行動畫
4.通過調用CALayer的addAnimation:forKey增加動畫到層(CALayer)中,這樣就能觸發動畫。通過調用removeAnimationForKey可以停止層中的動畫
5.Core Animation的動畫執行過程都是後臺操作的,不會阻塞主線程
2⃣ 屬性
1.duration:動畫的持續時間
2.repeatCount:重覆次數(HUGE_VALF、MAX FLOAT無限重覆)
3.repeatDuration:重覆時間(用的很少)
4.removedOnCompletion:預設為Yes。動畫執行完後預設會從圖層刪除掉
5.fillMode
6.biginTime
7.timingFunction:速度控制函數,控制動畫節奏
8.delegate
二、基礎動畫(CABasicAnimation)
如果只是實現簡單屬性變化的動畫效果,可以使用UIView的塊動畫替代基本動畫
1⃣ 屬性說明
-fromValue:keyPath相應屬性值的初始值
-toValue:keyPath相應屬性的結束值
2⃣ 動畫過程說明:
-隨著動畫的就行,在duration的持續時間內,keyPath相應的屬性值從fromValue漸漸變為toValue
-keyPath內容是CALayer的可動畫Animation屬性
-如果fillMode=kCAFillModeForwards同時removedOnCompletion=NO,那麼在動畫執行完畢後,圖層會保持顯示動畫執行後的狀態,但在實質上,圖層的屬性值還是動畫執行前的初始值,並沒有真正改變
3⃣ 代碼實現
位移需要考慮目標點設定的問題
三、關鍵幀動畫(CAKeyframeAnimation)
基礎動畫只能從一個值到另一個值,關鍵幀動畫可以用一個數組保存一系列值
1⃣ 屬性說明
-values:所有的值(用的較少)
-path:路線(如果設置了path,那麼values將被忽略)
-keyTimes:可以為對應的關鍵幀制定對應的時間點,取值範圍是0到1.0
2⃣ 過程步驟
-初始化自定義視圖
-點擊屏幕,執行動畫
1.指定點平移動畫(values)
2.路徑平移動畫(path C語言框架CGMutablePathRef,需要手動釋放記憶體)
3.貝塞爾路徑動畫(OC框架UIBezierPath)
4.搖晃動畫(修改旋轉角度)
3⃣ 代碼重構
如上代碼,重構在了一起
四、動畫組
動畫是可以併發執行的
-定義一個group組
-定義動畫
-將動畫加入group組
-可以給組設置屬性
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInView:self.view]; [_demoView setBackgroundColor:[UIColor redColor]]; // 1. 定義動畫組 CAAnimationGroup *group = [CAAnimationGroup animation]; // 定義一組動畫 // 淡入淡出動畫 CABasicAnimation *alpha = [MyCAHelper myBasicAnimationWithType:kCAHelperAlphaAnimation duration:1.0 from:@(1.0) to:@(0.3) autoRevereses:YES]; // 旋轉動畫 CABasicAnimation *rotation = [MyCAHelper myBasicAnimationWithType:kCAHelperRotationAnimation duration:2.0 from:@(-M_PI_2) to:@(M_PI_2) autoRevereses:NO]; // 縮放動畫 CABasicAnimation *scale = [MyCAHelper myBasicAnimationWithType:kCAHelperScaleAnimation duration:0.5 from:@(1.0) to:@(0.1) autoRevereses:YES]; // 關鍵幀路徑動畫,彈力模擬動畫效果 CAKeyframeAnimation *path = [self test1:_demoView.center to:location]; // 2. 設置動畫組屬性 [group setAnimations:@[alpha, path, rotation, scale]]; // 設置動畫的時長 [group setDuration:4.0]; // 3. 將動畫組添加到圖層 [_demoView.layer addAnimation:group forKey:nil]; }
五、轉場動畫-CATransition
1⃣屬性說明
-type:動畫過渡類型
-subtype:動畫過渡方向
-startProgress:動畫起點(在整體動畫的百分比)
-endProgress:動畫終點(在整體動畫的百分比)
-增加一個轉場演示視圖
-增加輕掃手勢
-在輕掃手勢方法中
1.更改演示視圖內容
2.創建轉場動畫效果
3.將轉場動畫添加到視圖的圖層
// 輕掃手勢操作 - (void)swipeAction:(UISwipeGestureRecognizer *)sender { // 通過輕掃手勢,讓切換出來的視圖是藍色的 if (_demoView.tag == 0) { [_demoView setBackgroundColor:[UIColor blueColor]]; [_demoView setTag:1]; } else { [_demoView setBackgroundColor:[UIColor redColor]]; [_demoView setTag:0]; } // 根據視圖內容我們來實現專場動畫 CATransition *anim = [CATransition animation]; // 設置專場動畫的過渡類型 [anim setType:@"cameraIrisHollowClose"]; // 需要根據手勢的方向,來決定專場動畫的動畫方向 // 註意:在轉場動畫中,動畫方向的左右是和手勢的方向相反的 if (sender.direction == UISwipeGestureRecognizerDirectionLeft) { [anim setSubtype:kCATransitionFromRight]; } else { [anim setSubtype:kCATransitionFromLeft]; } [_demoView.layer addAnimation:anim forKey:nil]; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 1. 實例化自定義視圖 _demoView = [[UIView alloc]initWithFrame:self.view.bounds]; [_demoView setBackgroundColor:[UIColor redColor]]; [self.view addSubview:_demoView]; // 2. 增加輕掃手勢 UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeAction:)]; [swipeLeft setDirection:UISwipeGestureRecognizerDirectionLeft]; [_demoView addGestureRecognizer:swipeLeft]; UISwipeGestureRecognizer *swipeRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeAction:)]; [swipeRight setDirection:UISwipeGestureRecognizerDirectionRight]; [_demoView addGestureRecognizer:swipeRight]; }
六、UIView的轉場動畫-雙視圖
+(void)transitionWithView:(UIView*)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void(^)(void))animations completion:(void(^)(BOOL finished))completion; 1⃣參數說明 -duration:動畫的持續時間 -view:需要進行轉場動畫的視圖 -options:轉場動畫的類型 -animations:將改變視圖屬性的代碼放在這個Block里 -completion:動畫結束後,自動調用的Block @interface ViewController () { UIImageView *_demoImageView; UIImageView *_demoImageView2; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 實例化第一個UIImageView的對象 _demoImageView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"1.jpg"]]; [self.view addSubview:_demoImageView]; // 實例化第二個UIImageView對象 // 註意:在雙視圖轉場動畫中,不要將第二個視圖添加到主視圖 _demoImageView2 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"2.jpg"]]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 單擊屏幕的時候,實現轉場動畫效果 [self animation1]; } #pragma mark - 單視圖的轉場動畫 - (void)animation2 { UIImageView *from; UIImageView *to; if (_demoImageView.superview) { from = _demoImageView; to = _demoImageView2; } else { from = _demoImageView2; to = _demoImageView; } [UIView transitionFromView:from toView:to duration:1.0f options:UIViewAnimationOptionTransitionCrossDissolve completion:^(BOOL finished) { NSLog(@"image view1 的主視圖: %@", _demoImageView.superview); NSLog(@"image view2 的主視圖: %@", _demoImageView2.superview); }]; } #pragma mark - 單視圖的轉場動畫 - (void)animation1 { [UIView transitionWithView:_demoImageView duration:1.0f options:UIViewAnimationOptionTransitionCurlUp animations:^{ // 在動畫塊代碼中設置視圖內容變化 if (_demoImageView.tag == 0) { [_demoImageView setImage:[UIImage imageNamed:@"2.jpg"]]; [_demoImageView setTag:1]; } else { [_demoImageView setImage:[UIImage imageNamed:@"1.jpg"]]; [_demoImageView setTag:0]; } } completion:nil]; }