【iOS开发每日小笔记(十二)】仿Facebook登录界面 错误提示抖动 利用CAAnimation设置动画效果
这篇文章是我的【iOS开发每日小笔记】系列中的一片,记录的是今天在开发工作中遇到的,可以用很短的文章或很小的demo演示解释出来的小心得小技巧。它们可能会给用户体验、代码效率得到一些提升,或是之前自己没有接触过的技术,很开心的学到了,放在这里得瑟一下。90%的作用是帮助自己回顾、记忆、复习。
原本以为国庆假期可以有时间看看书,写写博客。实际上大部分时间都被赶场参加婚礼和到处去亲戚家串门吃饭所占用。眼看明天还剩最后一天时间,今天赶紧来更新一篇,也算是没有完全荒废这7天长假吧!
Facebook的客户端的登录输入框,有一种很漂亮的效果,就是当用户输入的用户名和口令信息有误时,整个输入界面会左右晃动一下,提示用户“登入错误”,十分有意思。其实,这种效果实现起来也是很简单的。下面分享两种思路。
首先来看一下效果,当点击下方按钮时,灰色的UIView会左右晃动。
(gif图片效果不是太好,真机跑出来要更好一些。)
两种思路均是用CAAnimation实现,只不过一种使用绘制路径的方法为CAAnimation设置路径,另一种是利用“关键帧”设置value来实现。
1.1,路径法:
1 // 2 // ViewController.m 3 // ShakeInputViewDemo 4 // 5 // Created by pigpigdaddy on 14-10-6. 6 // Copyright (c) 2014年 pigpigdaddy. All rights reserved. 7 // 8 9 #import "ViewController.h" 10 11 @interface ViewController () 12 13 @property (nonatomic, strong)UIView *shakeView; 14 @property (nonatomic, strong)UIButton *button; 15 16 @end 17 18 @implementation ViewController 19 20 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil 21 { 22 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; 23 if (self) { 24 // Custom initialization 25 } 26 return self; 27 } 28 29 - (void)viewDidLoad 30 { 31 [super viewDidLoad]; 32 // Do any additional setup after loading the view. 33 34 self.button = [UIButton buttonWithType:UIButtonTypeSystem]; 35 [self.button setTitle:@"左右抖动" forState:UIControlStateNormal]; 36 self.button.frame = CGRectMake(100, 300, 100, 40); 37 [self.button addTarget:self action:@selector(shakeAction) forControlEvents:UIControlEventTouchUpInside]; 38 [self.view addSubview:self.button]; 39 40 self.shakeView = [[UIView alloc] initWithFrame:CGRectMake(50, 80, 220, 180)]; 41 self.shakeView.backgroundColor = [UIColor grayColor]; 42 [self.view addSubview:self.shakeView]; 43 } 44 45 - (void)didReceiveMemoryWarning 46 { 47 [super didReceiveMemoryWarning]; 48 // Dispose of any resources that can be recreated. 49 } 50 51 /* 52 #pragma mark - Navigation 53 54 // In a storyboard-based application, you will often want to do a little preparation before navigation 55 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 56 { 57 // Get the new view controller using [segue destinationViewController]. 58 // Pass the selected object to the new view controller. 59 } 60 */ 61 62 - (void)shakeAction 63 { 64 // 晃动次数 65 static int numberOfShakes = 4; 66 // 晃动幅度(相对于总宽度) 67 static float vigourOfShake = 0.04f; 68 // 晃动延续时常(秒) 69 static float durationOfShake = 0.5f; 70 71 CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 72 73 // 方法一:绘制路径 74 CGRect frame = self.shakeView.frame; 75 // 创建路径 76 CGMutablePathRef shakePath = CGPathCreateMutable(); 77 // 起始点 78 CGPathMoveToPoint(shakePath, NULL, CGRectGetMidX(frame), CGRectGetMidY(frame)); 79 for (int index = 0; index < numberOfShakes; index++) 80 { 81 // 添加晃动路径 幅度由大变小 82 CGPathAddLineToPoint(shakePath, NULL, CGRectGetMidX(frame) - frame.size.width * vigourOfShake*(1-(float)index/numberOfShakes),CGRectGetMidY(frame)); 83 CGPathAddLineToPoint(shakePath, NULL, CGRectGetMidX(frame) + frame.size.width * vigourOfShake*(1-(float)index/numberOfShakes),CGRectGetMidY(frame)); 84 } 85 // 闭合 86 CGPathCloseSubpath(shakePath); 87 shakeAnimation.path = shakePath; 88 shakeAnimation.duration = durationOfShake; 89 // 释放 90 CFRelease(shakePath); 91 92 [self.shakeView.layer addAnimation:shakeAnimation forKey:kCATransition]; 93 }
1.2,设置“关键帧”,让CAAnimation自动“补齐”帧与帧之间的动画,只需改变如下函数:
1 - (void)shakeAction 2 { 3 // 晃动次数 4 static int numberOfShakes = 4; 5 // 晃动幅度(相对于总宽度) 6 static float vigourOfShake = 0.04f; 7 // 晃动延续时常(秒) 8 static float durationOfShake = 0.5f; 9 10 CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 11 12 // 方法二:关键帧(点) 13 CGPoint layerPosition = self.shakeView.layer.position; 14 15 // 起始点 16 NSValue *value1=[NSValue valueWithCGPoint:self.shakeView.layer.position]; 17 // 关键点数组 18 NSMutableArray *values = [[NSMutableArray alloc] initWithObjects:value1, nil]; 19 for (int i = 0; i<numberOfShakes; i++) { 20 // 左右晃动的点 21 NSValue *valueLeft = [NSValue valueWithCGPoint:CGPointMake(layerPosition.x-self.shakeView.frame.size.width*vigourOfShake*(1-(float)i/numberOfShakes), layerPosition.y)]; 22 NSValue *valueRight = [NSValue valueWithCGPoint:CGPointMake(layerPosition.x+self.shakeView.frame.size.width*vigourOfShake*(1-(float)i/numberOfShakes), layerPosition.y)]; 23 24 [values addObject:valueLeft]; 25 [values addObject:valueRight]; 26 } 27 // 最后回归到起始点 28 [values addObject:value1]; 29 30 shakeAnimation.values = values; 31 shakeAnimation.duration = durationOfShake; 32 33 34 [self.shakeView.layer addAnimation:shakeAnimation forKey:kCATransition]; 35 }
两种方法的效果是一样的。
demo下载地址:https://github.com/pigpigdaddy/ShakeInputViewDemo