引言

最近公司的设计师很有空,时不时就来问一下某些动画效果好不好实现, 就学习了一下iOS的动画实现方法。
发现其实很多漂亮的动画效果都是几种基础的动画效果组合而成,总结一下基本的动画实现方法。并做了一个UIView扩展类。共同对抗设计师
地址:https://github.com/mozhenhau/D3View

一、动画方式

  1. UIView动画(Core Animation)
  2. CALayer绘画 (Core Graphics)

二、UIView动画

animateWithDuration

1
2
3
4
5
6
[UIView animateWithDuration:1 delay:0.5 options:UIViewAnimationOptionCurveEaseOut animations:^{
//要进行的动画区
_testView.alpha = 0;
} completion:^(BOOL finished) {
//动画完成后操作
}];

参数说明:

  • duration: 动画时长
  • delay: 决定了动画在延迟多久之后执行
  • options:用来决定动画的展示方式,接下来会进行讲解
  • animations:转化成动画表示的代码
  • completion:动画结束后执行的代码块

动画区有效的属性animations:

  1. 坐标尺寸(bounds,frame,center)、
  2. 视图显示(backgroundColor,alpha,hidden)
  3. 形态变化 (transform)

动画展示方式options:
看一下官方文档的展示方式有哪些:我看不懂的只能原文贴上了= 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  enum {
//默认,跟父类作为一个整体
UIViewAnimationOptionLayoutSubviews = 1 << 0,
//设置了这个,主线程可以接收点击事件
UIViewAnimationOptionAllowUserInteraction = 1 << 1,
//从当前状态开始动画,父层动画运动期间,开始子层动画。
UIViewAnimationOptionBeginFromCurrentState = 1 << 2,
//重复执行动画,从开始到结束, 结束后直接跳到开始态
UIViewAnimationOptionRepeat = 1 << 3,
//反向执行动画,结束后会再从结束态->开始态
UIViewAnimationOptionAutoreverse = 1 << 4,
//忽略继承自父层持续时间,使用自己持续时间(如果存在)
UIViewAnimationOptionOverrideInheritedDuration = 1 << 5,
//忽略继承自父层的线性效果,使用自己的线性效果(如果存在)
UIViewAnimationOptionOverrideInheritedCurve = 1 << 6,
//允许同一个view的多个动画同时进行
UIViewAnimationOptionAllowAnimatedContent = 1 << 7,
UIViewAnimationOptionShowHideTransitionViews = 1 << 8,
UIViewAnimationOptionOverrideInheritedOptions = 1 << 9,



- Curve速度变化方式
//开始慢,加速到中间,然后减慢到结束
UIViewAnimationOptionCurveEaseInOut = 0 << 16,
//开始慢,加速到结束
UIViewAnimationOptionCurveEaseIn = 1 << 16,
//开始快,减速到结束
UIViewAnimationOptionCurveEaseOut = 2 << 16,
//线性运动
UIViewAnimationOptionCurveLinear = 3 << 16,



- transitionWithView,转场方式
UIViewAnimationOptionTransitionNone = 0 << 20,
//从左往右翻页
UIViewAnimationOptionTransitionFlipFromLeft = 1 << 20,
//从右往左翻页
UIViewAnimationOptionTransitionFlipFromRight = 2 << 20,
//从下往上卷曲翻页
UIViewAnimationOptionTransitionCurlUp = 3 << 20,
//从上往下卷曲翻页
UIViewAnimationOptionTransitionCurlDown = 4 << 20,
////转场交叉消失,渐隐渐现
UIViewAnimationOptionTransitionCrossDissolve = 5 << 20,
//从上往下翻页
UIViewAnimationOptionTransitionFlipFromTop = 6 << 20,
//从下往上翻页
UIViewAnimationOptionTransitionFlipFromBottom = 7 << 20,
};
typedef NSUInteger UIViewAnimationOptions;

重点提一下这几个值,接下来使用下面的值做一下常用动画

  • UIViewAnimationOptionRepeat (从开始态到结束态重复执行)
  • UIViewAnimationOptionAutoreverse (结束后会反向再执行一次)
  • UIViewAnimationOptionCurveEaseInOut (慢开始加速->到中间后开始减速)
  • UIViewAnimationOptionCurveEaseIn (开始慢,加速到结束 )
  • UIViewAnimationOptionCurveEaseOut (开始快,减速到结束)
  • UIViewAnimationOptionCurveLinear (线性速度)

例子

  1. 这里通过这些属性实现一个上下来回的动画: =。=

    1
    2
    3
    [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationCurveEaseOut animations:^{
    _testView.frame = CGRectMake(135, -100, 50, 200);
    } completion:nil];

效果:

  1. 旋转动画:

    1
    2
    3
    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat |UIViewAnimationOptionCurveLinear animations:^{
    _testView.transform = CGAffineTransformMakeRotation(M_PI);
    } completion:nil];

效果:

  1. 上下来回和旋转动画一起:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse | UIViewAnimationCurveEaseOut animations:^{
    _testView.frame = CGRectMake(135, 200, 50, 50);
    }completion:nil];

    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionRepeat |UIViewAnimationOptionCurveLinear animations:^{
    _testView.transform = CGAffineTransformMakeRotation(M_PI);
    } completion:nil];

    } completion:nil];

效果:

复杂的动画效果都是通过集中简单的动画组合而成,可以尝试分解动画,然后一个个组合。

使用Spring Animation API (弹性动画)

Spring Animation 的 API 和一般动画相比多了两个参数,分别是usingSpringWithDamping和initialSpringVelocity。

  • usingSpringWithDamping参数
    usingSpringWithDamping的范围为0.0f到1.0f,数值越小「弹簧」的振动效果越明显. 下面引用别人家的图片

  • initialSpringVelocity 参数
    initialSpringVelocity则表示初始的速度,数值越大一开始移动越快。

使用Spring Animation能更好地实现自定义的动画效果,能自己定义开始速度,能方便地使用常用的弹性动画。

三、CABasicAnimation

例子

1
2
3
4
CABasicAnimation *animation = [CABasicAnimation
animationWithKeyPath:@”position”];
[animation setFromValue:[NSValue valueWithPoint:startPoint]]; [animation setToValue:[NSValue valueWithPoint:endPoint]]; [animation setDuration:5.0];
[layer addAnimation:animation forKey:nil];

animationWithKeyPath的所有动画类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
transform.scale = 比例轉換

transform.scale.x = 闊的比例轉換

transform.scale.y = 高的比例轉換

transform.rotation.z = 平面圖的旋轉

opacity = 透明度

margin

zPosition

backgroundColor 背景颜色

cornerRadius 圆角

borderWidth

bounds

contents

contentsRect

cornerRadius

frame

hidden

mask

masksToBounds

opacity

position

shadowColor

shadowOffset

shadowOpacity

shadowRadius

四、CALayer 绘图方式实现自定义动画

Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低级别、轻量级、高保真度的2D渲染。该框架可以用于基于路径的绘图、变换、颜色管理、脱屏渲染,模板、渐变、遮蔽、图像数据管理、图像的创建、遮罩以及PDF文档的创建、显示和分析。
iOS支持两套图形API族:Core Graphics/QuartZ 2D 和OpenGL ES。OpenGL ES是跨平台的图形API,属于OpenGL的一个简化版本。QuartZ 2D是苹果公司开发的一套API,它是Core Graphics Framework的一部分。

绘图步骤:

  1. 获取上下文
  2. 创建路径并设置路径
  3. 将属性添加到上下文
  4. 设置上下文属性
  5. 绘制路径
  6. 释放路径

使用方法:

  1. 新建一个文件,继承UIView
  2. 重写-(void)drawRect:(CGRect)rect方法
  3. 在drawRect方法内执行上面的绘图步骤

例子CALayer各种绘图

1.画直线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
-(void)drawRect:(CGRect)rect{
[self drawLine];
}

//画直线
-(void)drawLine{
CGContextRef ctx = UIGraphicsGetCurrentContext();
// CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor); //填充的颜色
// CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); //描边的颜色
// CGContextSetLineWidth(ctx, 5); //描边的宽度

//画线方法1,使用CGContextAddLineToPoint画线,需要先设置一个起始点
//设置起始点
CGContextMoveToPoint(ctx, 50, 50);
//添加一个点
CGContextAddLineToPoint(ctx, 100,50);

//画线方法2
//利用路径去画一组点
// CGMutablePathRef path = CGPathCreateMutable();
// CGPathMoveToPoint(path, nil, 0, 200);
// CGPathAddLineToPoint(path, nil, 100, 250);
// CGContextAddPath(ctx, path);

// CGContextFillPath(ctx); //填充
// CGPathRelease(path);
CGContextStrokePath(ctx); //描边
}

2.画圆

1
2
3
4
5
6
7
8
9
10
//画圆
-(void)drawRound{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(ctx, 100, 100);//移动画笔到指定坐标点
CGContextAddArc (ctx, 100, 100, 50, 0, 2*M_PI , 0);

//画椭圆,如果长宽相等就是圆
// CGContextAddEllipseInRect(ctx, CGRectMake(0, 250, 50, 50));
CGContextFillPath(ctx);
}

3.画矩形

1
2
3
4
5
6
//画矩形
-(void)drawRect{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddRect(ctx, CGRectMake(0, 250, 50, 50));
CGContextFillPath(ctx);
}

4.画多边形

1
2
3
4
5
6
7
8
9
10
11
12
//画多边形
-(void)drawPolygen{
CGContextRef ctx = UIGraphicsGetCurrentContext();

CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, nil, 120, 250);
CGPathAddLineToPoint(path, nil, 200, 250);
CGPathAddLineToPoint(path, nil, 180, 300);
CGPathCloseSubpath(path); //关键点,封闭路径,首尾连接
CGContextAddPath(ctx, path);
CGContextFillPath(ctx);
}

5.画图片

1
2
3
4
5
6
//画图片
-(void)drawImage{
// CGContextRef ctx = UIGraphicsGetCurrentContext();
UIImage *image = [UIImage imageNamed:@"test"];
[image drawInRect:CGRectMake(0, 0, 100, 100)];
}

6.画文字

1
2
3
4
5
6
7
8
//画文字
-(void)drawText{
//文字样式
UIFont *font = [UIFont systemFontOfSize:18];
NSDictionary *dict = @{NSFontAttributeName:font,
NSForegroundColorAttributeName:[UIColor blueColor]};
[@"hello world" drawInRect:CGRectMake(120 , 350, 500, 50) withAttributes:dict];
}

7.画扇形

1
2
3
4
5
6
7
8
//画扇形
-(void)drawArc{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextMoveToPoint(ctx, 100, 100);//移动画笔到指定坐标点
CGContextSetFillColorWithColor(ctx, [UIColor redColor].CGColor);//填充颜色
CGContextAddArc(ctx, 100, 100, 50, 1.5*M_PI, 0, 0); //添加一个圆,设置角度. 最后一个值0为顺时针,1为逆时针
CGContextFillPath(ctx);
}

总结

=.=要做什么高度定制动画可以自己绘制去, 复杂动画逐步分解。
实在想不到怎么做, 那就甩锅让UI做gif或者svg。

一个轻量的基础动画库:

https://github.com/mozhenhau/D3View