iOS 动画
2021-01-28 本文已影响0人
望穿秋水小作坊
一、动画基本知识
1. iOS动画(或者所有动画)的原理简单来讲有两种,是哪两种?
- ① 告诉系统动画对象在某几个时刻的状态(关键帧),由系统自动补全这些时刻之间的中间状态,再把所有这些状态平滑地显示出来。这种动画也叫做关键帧动画。
- ② 每隔一段很短的时间,重新绘制一次动画对象。
2. 下面是一张 iOS 动画相关 framework 的架构图,请说出 UIKit/AppKit、Core Animation、Core Graphics 的难度和灵活度?
image.png
从图中可以看到,要实现一个 iOS 动画从上层到底层,你会接触到 UIKit、Core Animation、Core Graphics。他们的使用难度一次递增,相应地灵活程度以及能实现效果的复杂程度也越来越高。
3. CALayer 有很多有用的子类,说几个常用的?
- CAShapeLayer:用于绘制曲线等图形
- CATextLayer:专门用来处理文字的 layer
- CAGradientLayer:顾名思义,用来处理渐变
4. UIView 和 CALyer 是什么关系?(重要,至少说 2 点)
- UIView 可以响应事件;CALayer 不可以
- UIView 主要是对显示内容的管理;CALayer 主要侧重显示内容的绘制
- UIView 是 iOS 平台对 CALayer 的封装;CALayer 是 iOS 和 OSX 跨平台的类
- UIView 的很多属性其实是返回 CALayer 的属性,比如 frame、center等等
二、 UIView 的 Block 动画
1. UIView 的 Block 动画的适用范围
- 适用于简单的动画,如移动、旋转、缩放、改变颜色等
2. 使用 Block 实现普通动画,请问下面的 NSLog(@"Animating");什么时间点被执行?
[UIView animateWithDuration:2.0 delay:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.redView.center = CGPointMake(200, 300);
NSLog(@"Animating");
} completion:^(BOOL finished) {
NSLog(@"finished");
}];
-
NSLog(@"Animating");会被立刻执行,并不会等到 1s 后;因为 Block 动画会在代码执行到的时候,立刻进行快照,所以说 Block 也不会去持有self
3. 使用 Block 实现关键帧动画
[UIView animateKeyframesWithDuration:6.f
delay:0.0
options:UIViewKeyframeAnimationOptionCalculationModeLinear
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 // 相对于6秒所开始的时间(第0秒开始动画)
relativeDuration:1/3.0 // 相对于6秒动画的持续时间(动画持续2秒)
animations:^{
self.view.backgroundColor = [UIColor redColor];
}];
[UIView addKeyframeWithRelativeStartTime:1/3.0 // 相对于6秒所开始的时间(第2秒开始动画)
relativeDuration:1/3.0 // 相对于6秒动画的持续时间(动画持续2秒)
animations:^{
self.view.backgroundColor = [UIColor yellowColor];
}];
[UIView addKeyframeWithRelativeStartTime:2/3.0 // 相对于6秒所开始的时间(第4秒开始动画)
relativeDuration:1/3.0 // 相对于6秒动画的持续时间(动画持续2秒)
animations:^{
self.view.backgroundColor = [UIColor greenColor];
}];
}
completion:^(BOOL finished) {
NSLog(@"finished");
}];
4. 思考下面代码,每一句打印是在什么时间点?
self.destinationPoint = CGPointMake(300, 300);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"dispatch_after");
self.destinationPoint = CGPointMake(-200, -200);
});
[UIView animateWithDuration:2.0 delay: 4.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.redView.center = self.destinationPoint;
NSLog(@"Animating");
} completion:^(BOOL finished) {
NSLog(@"finished");
}];
- dispatch_after在 2s 后被打印,再过 2s 会执行动画,最终 View 会移动到 CGPointMake(300, 300)的位置
三、 Core Animation 动画
1. Core Animation 动画的适用场景?
- 当我们无法用 Block 动画解决动画问题时,就是 Core Animation 出场了
- 比如:改变 cornerRadius,沿着一条曲线移动 view 等等
2. Core Animation 的所有功能都是基于 layer 对吗?
- 对,都基于 layer
- 当你通过 Core Animation 修改了 layer 的属性时,Core Animation 会通过
GPU 来重新渲染位图,因为是硬件渲染,因此速度非常快。
3. Core Animation 实际维护了三个 layer,是哪三个?分别有什么用?
- modelLayer:动画过程它的属性时不会变化的,这也是为什么动画结束默认会回到最初始状态的原因
- presentationLayer:如果你想在动画过程中实时拿到动画位置属性,就需要通过这个 layer 来获取
- renderLayer:是系统私有的
4. Timing Function 是用来干什么的?
- Timing function 用来描绘动画完成度随时间增加的曲线
5. CABasicAnimation 示例
CABasicAnimation *baseAnimation = [CABasicAnimation animationWithKeyPath:@"position.x"];
baseAnimation.fromValue = @10;
baseAnimation.toValue = @300;
baseAnimation.duration = 1.0;
baseAnimation.fillMode = kCAFillModeForwards; // fillMode 属性为 kCAFillModeForward 以留在最终状态
baseAnimation.removedOnCompletion = NO; // removedOnCompletion 为 NO 以防止它被自动移除
[self.rocketImageView.layer addAnimation:baseAnimation forKey:@"baseAnimation"];
3. CAKeyframeAnimation 路径动画示例
self.rocketImageView.center = self.cycleView.center;
CGRect boundingRect = CGRectMake(-150, -150, 300, 300);
CAKeyframeAnimation *orbit = [CAKeyframeAnimation animation];
orbit.keyPath = @"position";
/// 使用 CGPathCreateWithEllipseInRect(),我们创建一个圆形的 CGPath 作为我们的关键帧动画的 path。
orbit.path = CFAutorelease(CGPathCreateWithEllipseInRect(boundingRect, NULL));
orbit.duration = 4;
/// 连续动画必须要设置为YES,让下一个动画,基于上一个动画
orbit.additive = YES;
orbit.repeatCount = HUGE_VALF;
/// 使用 calculationMode 是控制关键帧动画时间的另一种方法。
/// 我们通过将其设置为 kCAAnimationPaced,让 Core Animation 向被驱动的对象施加一个恒定速度,
/// 不管路径的各个线段有多长。将其设置为 kCAAnimationPaced 将无视所有我们已经设置的 keyTimes。
orbit.calculationMode = kCAAnimationPaced;
/// 设置 rotationMode 属性为 kCAAnimationRotateAuto 确保飞船沿着路径旋转。
/// 作为对比,如果我们将该属性设置为 nil 那动画会是什么样的呢。
orbit.rotationMode = kCAAnimationRotateAuto;
// orbit.rotationMode = nil;
[self.rocketImageView.layer addAnimation:orbit forKey:@"orbit"];
6. CAAnimationGroup 动画示例
CABasicAnimation *zPositon = [CABasicAnimation animation];
zPositon.keyPath = @"zPosition";
zPositon.fromValue = @-1;
zPositon.toValue = @1;
zPositon.duration = 1.2;
CAKeyframeAnimation *rotation = [CAKeyframeAnimation animation];
rotation.keyPath = @"transform.rotation";
rotation.values = @[@0, @0.14, @0];
rotation.duration = 1.2;
rotation.timingFunctions = @[
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],
];
CAKeyframeAnimation *position = [CAKeyframeAnimation animation];
position.keyPath = @"position";
position.values = @[
[NSValue valueWithCGPoint:CGPointZero],
[NSValue valueWithCGPoint:CGPointMake(110, -20)],
[NSValue valueWithCGPoint:CGPointZero]
];
position.timingFunctions = @[
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]
];
position.additive = YES;
position.duration = 1.2;
CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
group.animations = @[zPositon, rotation, position];
group.duration = 1.2;
// group.beginTime = 0.5;
[self.oneImageView.layer addAnimation:group forKey:@"shuffle"];
self.oneImageView.layer.zPosition = 1;