CoreAnimation是一组非常强大 的API,它可以做出来非常绚丽的动画效果,前边介绍了 CoreAnimation 的核心组件CALayer;
CAAnimation简介:是所有动画对象的父类,负责控制动画的持续时间和速度,是个抽象类,不能直接使用,应该使用它具体的子类;
常用的属性:
属性说明:(红色代表来自CAMediaTiming协议的属性)
duration:动画的持续时间
repeatCount:重复次数,无限循环可以设置HUGE_VALF或者MAXFLOAT
repeatDuration:重复时间
removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode为kCAFillModeForwards
fillMode:决定当前对象在非active时间段的行为。比如动画开始之前或者动画结束之后
beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间
timingFunction:速度控制函数,控制动画运行的节奏
delegate:动画代理
话不多说,上代码:
// 1. 初始化动画对象 基本动画
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"position"];
// 设置代理
anim.delegate = self;
// 2. 设置动画属性
[anim setToValue:[NSValue valueWithCGPoint:toValue]];
[anim setDuration:1.0f];
// 4. 添加动画到图层
[self.myView.layer addAnimation:anim forKey:nil];
点击屏幕,动画执行完成,后又返回初始位置,怎么能让动画停留在最后点击位置呢? 这时候 fillMode 跟 removedOnCompletion 就派上用场了;
anim.fillMode = kCAFillModeForwards; //逐渐逼近目标点
anim.removedOnCompletion = NO;
fillMode属性值(要想fillMode有效,最好设置removedOnCompletion = NO)
kCAFillModeRemoved 这个是默认值,也就是说当动画开始前和动画结束后,动画对layer都没有影响,动画结束后,layer会恢复到之前的状态
kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态
kCAFillModeBackwards 在动画开始前,只需要将动画加入了一个layer,layer便立即进入动画的初始状态并等待动画开始。
kCAFillModeBoth 这个其实就是上面两个的合成.动画加入后开始之前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态
设置完之后,在运行,发现myView可以停留在手指点击后的位置了。 但是 很奇怪, view的Frame没有任何变化 ,可以用
NSLog(@"%@",NSStringFromCGRect(self.myView.frame));
在动画结束后,打印view的Frame观察
想要解决这个问题,可以使用setValue:forKey:传值
[anim setValue:[NSValue valueWithCGPoint:toValue] forKey:@"targetPoint1"];
[anim setValue:@"translationTo" forKey:@"animationType"];
然后在- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag 中设置 value的cener属性,修改坐标。
CGPoint targetPoint = [[anim valueForKey:@"targetPoint1"] CGPointValue];
NSLog(@"tragetPoint:%@",NSStringFromCGPoint(targetPoint));
完之后,在观察view的frame, 完美解决;
起始UIView帮我们封装了CoreAnimation的基本动画, 使用起来也非常容易:
[UIView animateWithDuration:1.0f animations:^{
self.myView.center = location;
} completion:^(BOOL finished) {
NSLog(@"%@",NSStringFromCGRect(self.myView.frame));
}];
但是,只是封装了一些基本动画, 如果我们要实现比较复杂的动画(CAAnimationGroup),还是需要实现上面一坨代码。
缩放动画示例:
- (void)scaleAnim{
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
anim.toValue = @0.5;
anim.duration = 0.5;
anim.autoreverses = YES; //自动翻转
[self.myView.layer addAnimation:anim forKey:nil];
}
旋转动画:
- (void)rotationAnim{
self.myView.layer.anchorPoint = CGPointMake(0.8, 0.8);
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
//不停地旋转
anim.toValue = @(2 * M_PI);
anim.repeatCount = HUGE_VALF;
anim.duration = 0.5;
//对于循环播放的动画效果,一定要removedOnCompletion设置为 No
anim.removedOnCompletion = NO;
[self.myView.layer addAnimation:anim forKey:@"rotationAnim"];
}
暂停动画:
- (void)pauseAnim{
//1. 取出当前的动画时间,要暂停的时间 CoreAnimation absolute time
CFTimeInterval currentTime = [self.myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
//2. 将动画的运行速度设置 == 0 ==》动画默认的运行速度是 1.0 /速度的比例
self.myView.layer.timeOffset = currentTime;
//3. 设置动画的时间偏移量 =》 让动画定格在该时间点
self.myView.layer.speed = 0.0f;
}
恢复动画:
- (void)continueAnim{
//1. 将动画的时间偏移量作为暂停时的时间点
CFTimeInterval pauseTime = self.myView.layer.timeOffset;
//2. 根据媒体时间计算出准确的启动动画时间 , 对之前的暂停动画时间进行修正
CFTimeInterval beginTime = CACurrentMediaTime() - pauseTime;
//3. 设置图层的开始动画时间
self.myView.layer.beginTime = beginTime;
//4. 将timeOffset清空
self.myView.layer.timeOffset = 0.0f;
self.myView.layer.speed = 1.0f;
}
在appdelegate中获取controller的方法:
ViewController *viewController = (ViewController *)[application.windows.firstObject rootViewController];