一)UIView动画
二)CoreAnimation动画
一、CoreAnimation动画之CALayer的使用
Layer动画和UIView动画十分相似。只需设置属性的初始值、结束值和动画执行时间,CoreAnimation会帮我们完成动画的中间过程。相对于上一篇的UIView动画,CALayer动画有着更多可设置的属性(如下列表),而且CALayer的很多子类还额外添加了更多属性(如CAShapeLayer、CAGradientLayer等),因此可实现更多的动画效果。
1.位置和大小
bounds
position
tranform:移动、缩放、旋转
2.边框
borderColor:边框颜色
borderWidth:边框宽度
cornerRadius:圆角
3.阴影
shadowOffset
shadowOpacity
shadowPath
shadowRadius
4.内容
contents
mask
opacity
首先,我们用layer动画实现一个简单的登录页面:
UITextField *username = [[UITextField alloc] initWithFrame:CGRectMake( -SCREENW -TEXTFIELDW , 100, TEXTFIELDW, TEXTFIELDH)];
[username setBorderStyle:UITextBorderStyleRoundedRect];
[username setPlaceholder:@"请输入用户名"];
_username = username;
[self.view addSubview:username];
UITextField *password = [[UITextField alloc] initWithFrame:CGRectMake(-SCREENW -TEXTFIELDW, 160, TEXTFIELDW, TEXTFIELDH)];
[password setBorderStyle:UITextBorderStyleRoundedRect];
[password setPlaceholder:@"请输入密码"];
password.delegate = self;
_passwork = password;
[self.view addSubview:password];
//在iOS中如果使用普通的动画则可以使用UIKit提供的动画方式来实现,如果想实现更复杂的效果,则使用Core Animation
//Core Animation中的Animation只是普通的数据模型,只要创建模型的实例对象,直接设置对象的属性即可。
//CABasicAnimation描述了layer动画,通过keyPath参数来指定需要改变的属性,然后设置对应属性的fromValue和toValue,最后,设置动画的持续时间。
CABasicAnimation *flyRight = [CABasicAnimation animationWithKeyPath:@"position.x"];
flyRight.fromValue = @(username.frame.origin.x);
flyRight.toValue = @(SCREENW*0.5);
flyRight.duration = 2.0;
flyRight.fillMode = kCAFillModeBoth;
//现在动画已经设置好了,可以将它设置给layer:
[username.layer addAnimation:flyRight forKey:nil];
[password.layer addAnimation:flyRight forKey:nil];
现在两个输入框确实以我们设定的动画形式出现,不过当动画结束后,输入框也随之不见了。这是为什么呢?
动画执行的时候,并非textfield本身进行移动变化,而是它的替身--呈现层(presentation layer)。既在动画开始时,textfield会隐藏,呈现层出现,在动画结束时呈现层从屏幕中移除,textfield重新变成可见,但还是在初始位置上。为了让动画停留在结束后的位置上,需要用到CABasicAnimation的removedOnCompletion属性:
flyRight.removedOnCompletion = NO;
//同时设置
flyRight.fillMode = kCAFillModeBoth;
好了,现在再看效果,动画结束后,输入框保留在最终的位置上!
不过现在有出现了一个问题,就是点击输入框,并没有弹出键盘。因为你点击的只是textfield的替身,并不能响应用户事件。为了解决这个问题,可以移除动画,并且显示真实的的textfield、更新textfield的位置属性,所以取消设置removedOnCompletion属性,改为设置textfield的位置属性:
username.layer.position = CGPointMake(SCREENW*0.5, username.layer.position.y);
password.layer.position = CGPointMake(SCREENW*0.5, password.layer.position.y);
现在运行,可以看到在动画结束之后,输入框保留在最终位置上,并且可以与用户进行交互了!
二、动画代理
有时我们希望知道动画什么时候开始或结束,可以通过动画代理实现:
fly.delegate = self;
实现动画开始和结束的方法:
- (void)animationDidStart:(CAAnimation *)anim{
NSLog(@"start");
}
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"stop");
}
运行代码,在动画开始和结束时,会有对应的Log输出。
不过一个CABasicAnimation对象可以添加到多个CALayer对象上,要怎么确定是哪个layer动画的回调呢?CAAnimation类和其子类遵守KVC,也就是在运行时,你可以像字典一样,给它设置新的属性。
[fly setValue:@"form" forKey:@"name"];
[fly setValue:login.layer forKey:@"layer"];
上面的代码给fly设置了key为name,值为form的属性,现在可以在代理回调方法中根据name的值,从而区分不同的动画。
同时设置了key为layer,值为view.layer的属性,因此可以在回调中获取到动画对应的layer。
例如,在动画结束时,加上放大动画:
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
NSLog(@"stop");
NSString *name = [anim valueForKey:@"name"];
if([name isEqualToString:@"form"]){
CALayer *layer = [anim valueForKey:@"layer"];
CABasicAnimation *pulse = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
pulse.fromValue = @(1.25);
pulse.toValue = @(1.0);
pulse.duration = 0.4;
[layer addAnimation:pulse forKey:nil];
}
}
注意到,创建动画时,我们使用的是animationWithKeyPath:方法,在任意时刻,可以根据这里设置的key,取消动画。
[info.layer removeAnimationForKey:@"infoappear"];
三、CAAnimationGroup动画组
动画组CAAnimationGroup继承自CAAnimation,可以保存一组动画对象,将CAAnimationGroup加入CALayer之后,可以同时执行多个动画效果(也可以通过设置单个动画的beginTime来分时执行)
CAAnimationGroup *gruopAnimation = [CAAnimationGroup animation];
gruopAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
gruopAnimation.duration = 0.5;
gruopAnimation.fillMode = kCAFillModeBackwards;
CABasicAnimation *scaleDown = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
scaleDown.fromValue = @(3.5);
scaleDown.toValue = @(1.0);
CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotate.fromValue = @(M_PI_4);
rotate.toValue = @(0.0);
CABasicAnimation *fade = [CABasicAnimation animationWithKeyPath:@"transform.opacity"];
fade.fromValue = @(0.0);
fade.toValue = @(1.0);
gruopAnimation.animations = @[scaleDown,rotate,fade];
[login.layer addAnimation:gruopAnimation forKey:nil];
四、CoreAnimation实现弹簧效果
在上一篇文章介绍的UIView.animateWith。。。。方法中,我们仅仅能设置阻尼(damping)和初始速度(inital velocity)两个变量,因此有时候看起来并不十分自然。而可以通过CoreAnimation的CASpringAnimation类来实现更真实的弹簧效果,可设置的属性有:
damping - 阻尼(默认10.0)
mass- 质量(默认1.0)
stiffness-刚度(默认100.0)
inital velocity-初始速度(0.0)
因为CASpringAnimation是CABasicAnimation的子类,所以用法是一样的:
CASpringAnimation *spring = [CASpringAnimation animationWithKeyPath:@"transform.scale"];
spring.beginTime = CACurrentMediaTime()+0.8;
spring.damping = 2.0;
spring.fromValue = @(1.25);
spring.toValue = @(1.0);
spring.duration = spring.settlingDuration;
[login.layer addAnimation:spring forKey:nil];
五、CAKeyframeAnimation
基础动画设置了fromValue和toValue,CoreAnimation根据这两个参数,在指定的一段时间内逐步改变指定的属性值。例如,当想要将layer从45°旋转到-45°时,只需要设置这两个角度值,动画过程中的值由CoreAnimation去处理。
CAKeyframeAnimation使用values属性代替了fromValue和toValue,values保存的是动画的转折点。另外还需要提供到达每个转折点的时间。
CAKeyframeAnimation *wobble = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];
wobble.duration = 0.25;
wobble.repeatCount = 4;
wobble.values = @[@(0.0), @(-M_PI_4/4), @(0.0), @(M_PI_4/4), @(0.0)];
wobble.keyTimes = @[@(0.0),@(0.25),@(0.5),@(0.75),@(1.0)];
[self.testView.layer addAnimation:wobble forKey:nil];
*如果需要改变的属性值为struct类型,例如position的类型为CGPoint,transform的类型为CATransform3D,bounds值为CGRect,均需”包装”成NSValue。
原文地址:http://blog.csdn.net/dolacmeng/article/details/52208367
demo代码:https://github.com/dolacmeng/AnimationDemo