美文网首页
iOS核心动画

iOS核心动画

作者: dc630f46ee2d | 来源:发表于2018-03-19 17:42 被阅读0次

1.谁来实现动画框架

3028657-fff6888a991ebe5d (1).jpeg
  • Core Animation 是一个框架,不仅仅只实现了动画功能,实际上它还实现了其他一些功能
  • QuartzCore又是什么? QuartzCore几乎等于Core Animation
    在CALayer类查看其定义,发现其定义在QuartzCore框架中而不是Core Animation框架,实际上QuartzCore几乎等于Core Animation,可以查看这个框架的主头文件中知道。
  #ifndef QUARTZCORE_H
  #define QUARTZCORE_H
  #include <QuartzCore/CoreAnimation.h>
  #endif /* QUARTZCORE_H */

Core Animation 以下

  • Core Animation 是更底层的两个框架OpenGL ES/OpenG,Core Graphic的封装
  • OpenGL ES/OpenGL : 第三方开源库
  • Core Graphic: 对苹果自己实现的绘图框架Quartz 2D的封装,Quartz 2D 是一组二维绘图和渲染API,而Core Graphic则是通过这组API实现绘图功能的。需要注意的是,它可以在iOS和Mac OS通用。

总结来说,OpenGL,Core Graphic实现了单张图的绘图(渲染)功能,而Core Animation实现了多张图的连续顺序渲染(动画)功能

Core Animation 以上

Core Animation中定义了CALayer类,实现了渲染功能,但是CALayer不能响应用户的操作,UIKit的UIView封装了CALayer类,由于继承了UIResponder,实现了用户操作的响应。
我们也可以UIView中使用类似的方法

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations

来实现动画,它是CoreAnimation中动画的封装,如果使用UIView可以完成需求,没有必要使用CoreAnimation的类来实现动画。

2. 参与动画的核心类

核心动画需要两种类来实现动画,一个是CAAnimation类,一个是CALayer类。

  • CAAnimation类视图层次
1644426-7d5b97696b996ed5.png

实际上,CAAnimation,CAPropertyAnimation是抽象类,因此实际使用中,我们一般使用CABasicAnimation,CAKeyframeAnimation,CAAnimationGroup,CATransition这四个动画类。

  • CALayer类
屏幕快照 2018-03-14 下午3.39.28.png

图上这些类基本继承CALayer.常用的是CALayer,CATextLayer,CAShapeLayer这三个类。
CALayer是真正实现显示功能的类。除了配合CAAnimation类来实现动画外,还有其他一些功能,并非本文研究的重点,暂时不表。

3.CABasicAnimation CALayer实现简单动画。

  • CABasicAnimation重要属性
    无论是CABasicAnimation还是CAKeyframeAnimation,他们都继承CAPropertyAnimation类。参照上面的图,CAPropertyAnimation中有一个重要的属性keyPath,keyPath表示对CALayer的哪个属性执行动画。所有的keyPath如下:
transform.translation.x = 平行移动
transform.scale = 比例轉換
transform.scale.x = 闊的比例轉換
transform.scale.y = 高的比例轉換
transform.rotation.z = 平面圖的旋轉
opacity = 透明度
margin = 布局
zPosition = 翻转
backgroundColor = 背景颜色
cornerRadius = 圆角
borderWidth = 边框宽
bounds = 大小
contents = 内容
contentsRect = 内容大小
cornerRadius = 圆角
hidden = 显示隐藏
mask
masksToBounds
opacity
position
shadowColor
shadowOffset
shadowOpacity
shadowRadius

CABasicAnimation 还需要指定keyPath初始化状态和结束状态,自己有两个属性fromValue,toValue(也可以设置fromValue,byValue,这时候toValue = fromValue+byValue)。一个动画必然有执行时间。在CAMediaTiming协议中定义了该属性duration.有时候我们需要动画有一个翻转回放效果,比如说先放大后按照回放缩小,autoreverse属性实现了回放效果。动画执行的时候可以是重复多次的,使用repeatCount来指定重复次数。动画有速度,还可以匀速,先快后慢或者先慢后快,timeFunction属性实现了动画的节奏。

  • CABasicAnimation执行动画的套路
  1. 创建一个CABasicAnimation对象同时指定其keyPath
  2. 设定CABasicAnimation其他属性。
  3. 使用layer的- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key
    UIView *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.backgroundColor = [UIColor redColor];
    button.frame = CGRectMake(40, 40, 200, 200);
    [self.view addSubview:button];
    
    CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    basicAnimation.fromValue = @0;
    basicAnimation.toValue = @1;
    basicAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
    basicAnimation.autoreverses = YES;
    basicAnimation.repeatCount = 1;
    basicAnimation.duration = 2;
    basicAnimation.fillMode = kCAFillModeForwards;
    [button.layer addAnimation:basicAnimation forKey:nil];

4.CAKeyFrameAnimation CALayer实现简单动画

CABasicAnimation 通过fromValue和toValue指定动画初始化状态和结束状态,框架自动根据这两个状态执行过渡的动画。CAKeyFrameAnimation可以指定多个状态,每个状态称之为关键帧。它有一个Values属性(数组)来保存多个状态。记录的是Keypath的不同时期的值。CABasicAnimation只有两个状态,这两个状态之间的速率使用一个timingFunction就可以了。而对于多个状态的动画,自然需要多个timingFunction来实现不同桢动画,因此定义了timingFunctions数组来完成两个桢之间的时间节奏。当我们再定义好duration后,似乎整个动画都完全被定义好了。但是我们也许还希望定义每个桢到达点的时间,这样就可以决定好各个动画段的时间了。
keyTimes定义了每个桢到达的时间比例。取值是@0到@1,@0.5表示duration * 0.5.

    UIView *button = [UIButton buttonWithType:UIButtonTypeCustom];
    button.backgroundColor = [UIColor redColor];
    button.frame = CGRectMake(40, 40, 200, 200);
    [self.view addSubview:button];
    CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale"];
    keyframeAnimation.values = @[@1, @0.5, @1];
    CAMediaTimingFunction *mediaTimingFunction1 = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    CAMediaTimingFunction *mediaTimingFunction3 = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    keyframeAnimation.timingFunctions = @[mediaTimingFunction1, mediaTimingFunction3];
    keyframeAnimation.repeatCount = 1;
    keyframeAnimation.duration = 3;
    keyframeAnimation.keyTimes = @[@0 ,@0.2, @1.0];
    keyframeAnimation.fillMode = kCAFillModeForwards;
    keyframeAnimation.autoreverses = NO;
    keyframeAnimation.repeatCount = 1;
    [button.layer addAnimation:keyframeAnimation forKey:nil];

5. CAKeyFrameAnimation的Paths属性实现动画

上一个例子中,CAKeyFrameAnimation是一个更精细的CABasicAnimation动画,可以定义多个时间段。CABasicAnimation也可以理解为values只有两个值的CAKeyFrameAnimation的版本。但是实际上CAKeyFrameAnimation能做的事情更多。再次观察一下上面CAAnimation视图层次图。发现CAKeyframeAnimation比CABasicAnimation多一个path属性(CGPathRef类型)。path规定了动画执行路径。path称为路径,指定path后,动画就沿着path执行,path是连续的而不是指定values离散的方式。path是一条线路,因为不是所有的keyPath属性都可以使用path.
当我们使用path属性后,values属性将不起作用
path的构造是一个比较复杂的过程,我们可以直接用更底层的CoreGraphic层的API来构造它,也可以使用UIkit层的UIBezierPath 构造UIkit的path后再通过[bezierPath CGPath]转换而来。前置是面向过程的API,后者是面向对象的API.

5.1构建CoreGraphic 的CGPathRef

相关构造方法都在CoreGraphics/CGPath.h文件中定义

  1. 创建一个path引用
  CGMutablePathRef path = CGPathCreateMutable();
  1. 设定动画的起始位置
    CGPathMoveToPoint(path, nil, 200, 200);
  1. 添加直线或者曲线或者弧线
  • 直线
  CGPathAddLineToPoint(path, nil, 300, 300);
  • 二阶贝塞尔曲线(抛物线)
    贝塞尔曲线 需要三个点(两条直线)来确定。分别是起点 ,终点和控制点。


    0_1282984428GQ1X.gif

    实际上,代码编写过程中,起点和终点是确定的,控制点比抛物线最高的还高一点的位置就可以了

CGPathAddQuadCurveToPoint(path, nil, 250, 50, 300, 300);

P0 是上一个API结束后的点,P1是控制点,对应(250,50),P2是终点,即是(300,300)

  • 三阶贝塞尔曲线(用的少)


    0_1282984443K2nb (1).gif
CGPathAddCurveToPoint(path, nil, 180, 100, 270, 100, 300, 200);
  • 圆弧(Arc)

圆弧和曲线只是定义方式不同,实际上从数学角度来说都是曲线。在iOS中Curve指的是用贝塞尔方式定义(几条线段)的曲线。Arc指的是用中心点和旋转角度定义的圆弧。

CGPathAddArc(path, nil, 100, 100, 100, 0,  350 * M_PI/ 180,NO);
  • 第三个第四个参数是圆弧的起点(在圆的最右边)
  • 第五个参数是圆的半径
  • 第六个参数是起始弧度(不是角度,开发者对角度认知比较直观,因此还需要使用
  • 第七个参数是结束弧度(不是角度,开发者对角度认知比较直观,一因此还需要角度转弧度的公式)
  • 0角度 == 0 弧度
  • x角度 == x * π/180 弧度 (π在oc中有一个宏M_PI)

4.释放资源
由于使用C的面向过程的API,使用结束后要自己回收资源
CGPathRelease(path);


    CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyframeAnimation.autoreverses = NO;
    keyframeAnimation.duration = 3;
    keyframeAnimation.removedOnCompletion = NO;
    keyframeAnimation.fillMode = kCAFillModeForwards;
    keyframeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    keyframeAnimation.repeatCount = 1;
    CGMutablePathRef path = CGPathCreateMutable();
    CGPathMoveToPoint(path, nil, 200, 100);
    CGPathAddArc(path, nil, 100, 100, 100, 40,  350 * M_PI/ 180,NO);
    keyframeAnimation.path = path;
    CGPathRelease(path);
    [button.layer addAnimation:keyframeAnimation forKey:nil];

注意: 面向过程的API都是有状态的,一个方法的终点是下个方法的起点。

5.2构建UIKit 的UIBezierPath 后使用CGPath
// Path construction

- (void)moveToPoint:(CGPoint)point;
- (void)addLineToPoint:(CGPoint)point;
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint;
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise NS_AVAILABLE_IOS(4_0);
- (void)closePath;  
- (void)removeAllPoints;

实际上,这些API和CGMutablePathRef相关的API类似,只不过结束的时候需要将UIBezierPath对象转化为CGMutablePathRef 即可

     keyframeAnimation.path = [path CGPath];

Demo如下

- (void)viewDidLoad {
    [super viewDidLoad];
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(195, 95, 10, 10)];
    view.backgroundColor = [UIColor blueColor];
    [self.view addSubview:view];
    
    CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyframeAnimation.autoreverses = NO;
    keyframeAnimation.duration = 1;
    keyframeAnimation.removedOnCompletion = NO;
    keyframeAnimation.fillMode = kCAFillModeForwards;
    keyframeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    keyframeAnimation.repeatCount = 1;
    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:CGPointMake(200, 100)];
    [path addArcWithCenter:CGPointMake(100, 100) radius:100 startAngle:0 endAngle:350 * M_PI/180 clockwise:YES];
    keyframeAnimation.path = [path CGPath];
    [button.layer addAnimation:keyframeAnimation forKey:nil];

6.动画组

一个CAAnimation只能对CALayer的一个属性起作用,如果需要同时对一个CALayer多个属性进行动画操作,那么就需要使用动画组了。

demo 放大某个视图同时逐渐隐藏它

    CABasicAnimation *basicAnimation1 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    basicAnimation1.removedOnCompletion = NO;
    basicAnimation1.fillMode = kCAFillModeForwards;
    basicAnimation1.duration = 2;
    basicAnimation1.repeatCount = 1;
    basicAnimation1.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    basicAnimation1.autoreverses = NO;
    basicAnimation1.fromValue = @0;
    basicAnimation1.toValue = @1;
    
    
    CABasicAnimation *basicAnimation2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
    basicAnimation2.removedOnCompletion = NO;
    basicAnimation2.fillMode = kCAFillModeForwards;
    basicAnimation2.duration = 2;
    basicAnimation2.repeatCount = 1;
    basicAnimation2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    basicAnimation2.autoreverses = NO;
    basicAnimation2.fromValue = @1;
    basicAnimation2.toValue = @0;
    
    CAAnimationGroup *group = [CAAnimationGroup animation];
    group.duration = 2;
    group.repeatCount = 1;
    group.timingFunction =  [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    group.fillMode = kCAFillModeForwards;
    group.removedOnCompletion = NO;
    group.animations = @[basicAnimation1, basicAnimation2];
    [button.layer addAnimation:group forKey:nil];

相关文章

网友评论

      本文标题:iOS核心动画

      本文链接:https://www.haomeiwen.com/subject/ueiefftx.html