# iOS开发动画篇之从UIView动画说起

语言：zh-CN
原文：[iOS开发动画篇之从UIView动画说起](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao09>)
中文：[中文 HTML](<https://allluckly.cn/%E6%8A%95%E7%A8%BF/tuogao09>)
English: [English HTML](<https://allluckly.cn/en/journal/2016-01-22-ios-animation-starting-with-uiview-animations/>)
发布于: 2016-01-22T00:00:00+08:00
内容更新于: 2016-01-22T00:00:00+08:00
署名：SindriLin

以输入框进入和按钮出现的登录动画为例，介绍 UIView 可动画属性、时间与选项参数，再加入弹簧动画，帮助理解位置、透明度与形变如何组合成连续效果。

原文署名SindriLin，Bison 编辑收录。

[原文所列来源](<http://sindrilin.com/animate/2016/01/22/%E4%BB%8EUIView%E5%8A%A8%E7%94%BB%E8%AF%B4%E8%B5%B7.html>)

**阅读前** 这是 2015–2017 年间的技术记录，本次整理未重新运行验证示例。涉及的 API、依赖和工具版本请结合发表日期阅读。

编辑：[Bison](<https://allluckly.cn/>)  
 投稿：[SindriLin’s 小巢](<http://sindrilin.com/animate/2016/01/22/%E4%BB%8EUIView%E5%8A%A8%E7%94%BB%E8%AF%B4%E8%B5%B7.html>)

> "本文作为动画文集的第一篇，最开始是想做个qq下拉刷新的水滴动画的制作讲解，但这几天研读《iOS Animations by Tutorials》一书，对iOS动画开发有了更为深刻的了解，于是决定动画篇将从UIView动画开始讲起，以后还会有Layer、Transitioning等在内的动画，希望本文能抛砖引玉，带给大家不一样的理解，下面送上一张书中demo的效果图。"

毋庸置疑的：在iOS开发中，制作动画效果是最让开发者享受的环节之一。一个设计严谨、精细的动画效果能给用户耳目一新的效果，吸引他们的眼光 —— 这对于app而言是非常重要的。

本文作为动画文集的第一篇，最开始是想做个qq下拉刷新的水滴动画的制作讲解，但这几天研读《iOS Animations by Tutorials》一书，对iOS动画开发有了更为深刻的了解，于是决定动画篇将从UIView动画开始讲起，以后还会有Layer、Transitioning等在内的动画，希望本文能抛砖引玉，带给大家不一样的理解，下面送上一张书中demo的效果图。[购买书籍](<http://www.raywenderlich.com/store>)

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/1.gif>)

*ps: 本文的内容主要为iOS开发的动画入门*

## 从登录动画说起

很长一段时间以来，我都在基于CALayer层进行动画实现，却忽略了UIKit提供给我们的动画接口。这些接口函数足够的强大并且十分的灵活，足以满足我们开发中大部分的动画需求。

在我们了解这些强大的接口前，我们先来看看第一个效果：在用户打开app要进行登录的时候，账户和密码输入框从屏幕的左边进入，接着登录按钮出现。

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/2.gif>)

在这段动画之中发生的最为明显的事情就是两个文本框的位置变化，在动画开始之前，两个文本框的位置应该是在屏幕的左边，而下方的按钮现在是隐藏状态（设置alpha）

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/3.jpg>)

因此，这个动画之中发生的事情，我们可以用概括为下面的代码：

```
self.userName.center.x += offset;    //userName进入
self.password.center.x += offset;    //password进入
self.login.alpha = 1;                //显示登录按钮
既然已经知道了我们的动画发生了什么，接着就可以使用`UIKit`的动画API让我们的动画活起来了

//设置文本框初始位置为屏幕左侧
CGPoint accountCenter = self.userName.center;
CGPoint psdCenter = self.password.center;
accountCenter.x -= 200;
pasCenter.x -= 200;
self.userName.center = accountCenter;
self.password.center = psdCenter;

//还原中心坐标
accountCenter.x += 200;
psdCenter.x += 200;        
[UIView animateWithDuration: 0.5 animations: ^{
    self.userName.center = accountCenter;
    self.password.center = passwordCenter;
    self.login.alpha = 1;
} completion: nil];
```

在UIKit中，系统提供了`animate`标题打头的属于`UIView`的类方法让我们可以轻松的制作动画效果，每一个这样的类方法提供了名为`animations`的`block`代码块，这些代码会在方法调用后立刻或者延迟一段时间以动画的方式执行。此外，所有这些API的第一个参数都是用来设置动画时长的。

在`viewDidAppear:`中运行这段代码，你会看到文本框从左侧滑动，按钮也渐变显示出来的，但是跟我们要的结果不太一样 —— 三个动画没有错开，效果并不那么的好看。我们希望密码框能在账户文本框滑动后的一段时间后再出现，按钮同样也需要晚一些显示。所以，我们需要使用下面的方法来实现这个效果：

```
[UIView animateWithDuration: 0.5 delay: 0.35 options: UIViewAnimationOptionCurveEaseInOut animations: ^{
    self.password.center = passwordCenter;
} completion: ^(BOOL finished) {
    [UIView animateWithDuration: 0.2 animations: ^{
        self.login.alpha = 1;
    }];
}];
```

这个方法看起来非常的熟悉，相比上面的方法这里多了几个参数来高度定制我们的动画：

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

在上面的代码中，密码输入框在延后0.35秒之后开始从左侧出来，在持续0.5秒的动画之后，开始渐变显示按钮，然后动画完成。

## 可实现动画的属性

现在你已经可以制作简单的动画了，但要记住：不是所有修改属性的操作放到`animations`代码块中都是变成动画实现的 —— 不管你怎么修改一个视图的`tag`，或者是`delegate`。因此，可实现动画的属性必定会导致视图的重新渲染。  
 这些可以生成动画的属性大致可以分成这么三类：`坐标尺寸`、`视图显示`、`形态变化`

#### 坐标尺寸类

- bounds：修改这个属性会结合`center`属性重新计算`frame`。建议通过这个属性修改尺寸
- frame：修改这个属性通常会导致视图形变的同时也发生移动，然后会重新设置`center`跟`bounds`属性
- center: 设置后视图会移动到一个新位置，修改后会结合`bounds`重新计算`frame`

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/4.jpg>)

#### 视图显示类

- backgroundColor: 修改这个属性会产生颜色渐变过渡的效果，本质上是系统不断修改了tintColor来实现的
- alpha：修改这个属性会产生淡入淡出的效果
- hidden：修改这个属性可以制作翻页隐藏的效果

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/5.jpg>)

#### 形态变化类

- transform：修改这个属性可以实现旋转、形变、移动、翻转等动画效果，其通过矩阵运算的方式来实现，因此更加强大

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/6.jpg>)

## 动画参数

上面我们使用到的动画方法中有一个重要的参数`options`，它能让你高度的自定义动画效果。下面展示这个参数类型的值集合，你可以通过结合不同的参数来实现自己的动画：

```
-  Repeating

UIViewAnimationOptionRepeat       //动画循环执行
UIViewAnimationOptionAutoreverse  //动画在执行完毕后会反方向再执行一次
我们将这两个参数传入到上面密码框出现动画中，看看会有什么效果（不同的参数使用`|`操作符一起传入）

[UIView animateWithDuration: 0.5 delay: 0.35 options: UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations: ^{
    self.password.center = passwordCenter;
} completion: ^(BOOL finished) {
    [UIView animateWithDuration: 0.2 animations: ^{
        self.login.alpha = 1;
    }];
}];
```

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/7.gif>)

我们可以看到密码框在不断的循环进入屏幕，反方向退出屏幕这个操作，并且登录按钮也始终没有渐变出现。由此可以知道` UIViewAnimationOptionRepeat  `参数不仅是让动画循环播放，并且还导致了`completion`的回调永远无法执行。

- Easing

我们都知道，一个好的动画应该更符合我们认知的规则。比如，任何事物都不能突然间的开始移动和停下，像车辆启动和停止都有一个加速和减速的过程。

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/8.jpg>)

为了让动画更具符合我们的认知，系统同样提供了类似的效果的参数给我们使用：

```

UIViewAnimationOptionCurveEaseInOut   //先加速后减速，默认
UIViewAnimationOptionCurveEaseIn      //由慢到快
UIViewAnimationOptionCurveEaseOut     //由快到慢
UIViewAnimationOptionCurveLinear      //匀速
我在demo上创建了四个橙色的UIView，分别传入这四个不同的参数，然后让这四个view在同一时间y轴上向上移动。

[self animatedView: _view1];
[self animatedView: _view2];
[self animatedView: _view3];
[self animatedView: _view4];

//y轴上移动视图上升250
- (void)animatedView: (UIView *)view
{
    [UIView animateWithDuration: 0.5 delay: 0 options: UIViewAnimationOptionCurveLinear animations: ^{
    CGPoint center = view.center;
    center.y -= 250;
    view.center = center;
    } completion: nil];
}

```

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/9.gif>)

在模拟器运行状态下，点击上面的菜单栏 `DEBUG -> Slow Animation` 或者快捷键 `command + T`，这会放慢我们app的动画运行速度（demo在6p的模拟器上运行）。  
 在减速环境下，我们看到四个view的速度变化如下：

```
1、逐渐加速。`EaseIn`
2、先加速，后减速。`EaseInOut`
3、速度领先，然后减速。`EaseOut`
4、匀速运动。`Linear`
```

运行最开始的登录动画，放慢模拟器的动画速度，你会看到默认情况下使用的`EaseInOut`参数使得密码框在接近结束点的时候出现了明显的减速动画。

```
- Transitioning
除了上面提到的这些效果，在视图、图片切换的时候，我们还能通过传入下面的这些参数来实现一些特殊的动画效果。
UIViewAnimationOptionTransitionNone            //没有效果，默认
UIViewAnimationOptionTransitionFlipFromLeft    //从左翻转效果
UIViewAnimationOptionTransitionFlipFromRight   //从右翻转效果
UIViewAnimationOptionTransitionCurlUp          //从上往下翻页
UIViewAnimationOptionTransitionCurlDown        //从下往上翻页
UIViewAnimationOptionTransitionCrossDissolve   //旧视图溶解过渡到下一个视图
UIViewAnimationOptionTransitionFlipFromTop     //从上翻转效果
UIViewAnimationOptionTransitionFlipFromBottom  //从上翻转效果
那么这些参数使用的时机是什么时候呢？我们来看看这么一段代码：
[UIView transitionWithView: firstPV duration: 0.5 options: UIViewAnimationOptionTransitionFlipFromLeft animations: ^{
    [firstPV flipCard];
} completion: ^(BOOL finished) {
    isAnimating = NO;
}];

- (void)flipCard
{
    if (isfliped) {
        self.image = [UIImage imageNamed: @"flipPicBG.png"];
        isfliped = NO;
    } else {
        self.image = [UIImage imageNamed: [NSString stringWithFormat: @"flipPic%d.png", type]];
        isfliped = YES; 
    }
}
```

这段代码中我改变了一个`UIImageView`的图片显示，同样用了一个动画的方式表现。这里用到了一个新的动画API方法，`transitionWithView: duration: options: animations: completion:`，这个方法跟上面的`animateWithDuration`系列方法相比多了一个`UIView`类型的参数，这个参数接收的对象作为动画的作用者。这段代码是我以前做的一个翻卡匹配的小游戏，点击之后的动画效果如下：

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/10.gif>)

在模拟器下使用`command+T`放慢了动画的速度之后，我截取了翻转的四张图片：

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/11.jpg>)

在我们切换图片的时候，原有的图片会基于视图中心位置进行x轴上的翻转，为了达到更逼真的效果，系统还为我们在切换中加上了阴影效果*（ps: 再次要说明的是，transition的动画你应该只用在视图的切换当中 —— 你不会在移动中产生任何transition效果的）*

## 弹簧动画

恭喜你，你已经可以使用`UIKit`的动画接口制作精美的动画了，通过组合不同的`options`参数你可以制作真实的动画。但是，我们总是能做的更多，比如一个弹簧被用力压扁，当松开手的时候会反复弹动。使用上面的方式纵然可以实现这样的动画，但代码量复杂，也基本无复用性可言，可想而知会是糟糕的代码。因此，我们需要其他的动画方式，系统也正好提供了这样的一种动画供我们使用：

```
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion
```

照例科普一下额外的参数信息：

```
- dampingRatio：速度衰减比例。取值范围0 ~ 1，值越低震动越强
- velocity：初始化速度，值越高则物品的速度越快

```

当一个圆角按钮高速移动的进入界面中，接着狠狠的震动，这绝对会狠狠地吸引住你的眼球。比如我尝试着让某个`UICollectionView`的分类按钮从屏幕下方弹入视图的时候；又或者我让这个小球弹到右下角，以提示用户该如何操作：

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/12.gif>)

这效果非常的棒，在看到这些小圆球之后，你本能的会想要去点击这些按钮，而这些小球弹出的动画仅仅需要下面这么几句代码：

```
CGPoint center = cell.center;
CGPoint startCenter = center;
startCenter.y += LXD_SCREEN_HEIGHT;
cell.center = startCenter;

[UIView animateWithDuration: 0.5 delay: 0.35 * indexPath.item usingSpringWithDamping: 0.6 initialSpringVelocity: 0 options: UIViewAnimationOptionCurveLinear animations: ^{
    cell.center = center;
} completion: nil];
```

除了这段弹出的代码，在小球被点击的时候，还会产生一个弹到右下角的动画，然后从左侧弹出列表。这非常的酷，因为不用额外的提示，用户会很自然的知道该怎么回到分组的界面 —— 点击一下右下角的圆形按钮。这是非常重要的，我们的动画应该不仅仅只是为了让界面更加的优雅漂亮，还应该能用以减少用户学习使用app的成本，这些都是动画的追求。

### 最后

相比起PC端的粗糙，移动端的应用需要更加精致，精致复杂的动画都是源于一个个简单的动画组合而成的。本文作为动画篇的第一篇博客，目的是为了从最简单的`UIView`动画讲起，慢慢的拓展其它的动画，也希望能起到抛砖引玉的作用。在文章的最后，如果你是iOS动画的初学者，请尝试结合上面提到的知识，为本文最开始的登录demo中添加代码，让按钮从下方渐变显示弹出：

![1](<https://allluckly.cn/images/blog/tuogao/tougao09/13.gif>)

[本文demo](<https://github.com/JustKeepRunning/LXDUIViewAnimation>)

---

好文推荐：[iOS开发之详解连连支付集成](<https://allluckly.cn/ios%E6%94%AF%E4%BB%98/lianlianzhifu>)

