时至今日,iOS 应用商店已经拥有超过了140万 应用,让你自己的应用脱颖而出确实是个不小的挑战。不过,在你的应用掉入默默无闻的大黑洞之前,你拥有一个小小的机遇窗,它能帮你吸引用户的注意。
想让你的用户喝彩尖叫,没有比应用加载界面更好的地方 ,在这个地方,你可以添加一个讨人喜欢的动画来作为你登陆或者认证流程的先导。
在这个教程中,你将要学会如何利用先进的技术来创建一个流畅并且迷人的动画。
开始吧!!
从这里下载启动项目,保存在一个合适的路径并用Xcode打开。
打开 HolderView.swift 。 在这个UIView 的子类中,你可以添加些子层(在Layers的下级目录中可以找到),使之像上面的动画一样生动
OvalLayer.swift: 这是第一层,它从零尺寸扩展,然后会有一小段时间的摇摆
TriangleLayer.swift: 接下来的这个层TriangleLayer会在OvalLayer 摇摆的时候出现,当此视图转动时,OvalLayer 会缩小到零尺寸,并在TriangleLayer 中消失。
RectangleLayer.swift: 这个层是TriangleLayer 用于分类的可视化容器
ArcLayer.swift: 这个层动画特效填充在RectangleLayer 中,这和杯子里填充了水(效果)非常相似
打开OvalLayer.swift, 启动项目已经包含了用于初始化这个层的代码和所有你会在动画里用到的Bezier path(对象)。你会看到expand(),wobble()和contract()方法都是空的。你可以通过参考这个指导书来填充这些方法。所有其他的 *Layer (以layer结尾)的文件都用了相似的方式构建。
注意:如果你想要学习更多的Bezier paths,那就检出我们系列指导书 Modern Core Graphics with Swift
最后,打开ViewController.swift 查看addHolderView()方法,这个方法添加了一个HolderView 作为一个子视图,放到viewcontroller 视图的中间。这个视图将会放置所有的动画。viewcontroller仅仅需要把它放到屏幕上,这个视图将会照管好现行的动画代码。
animateLabel() 是由类 HolderView 提供的代理回调函数,此类中你会用你完成的动画序列来填充。addButton()方法只是添加一个按钮到视图中,用于触摸和重启动画。
编译并运行你的应用;你会看到一个空白屏幕。一个空白的画布–这就是用于开始创建你的新动画的完美的载体。在指导书的最后,你的应用会看起来是这样的:
现在不需再费周折,我们开始吧
添加一个椭圆
这个动画从一个椭圆开始,椭圆是从屏幕中间扩展到视图,然后在周围有点摇摆。
打开 HolderView.swift ,在 HolderView 类的顶端附近声明如下的常量
1 |
let ovalLayer = OvalLayer() |
现在在此类的底部添加如下方法
1 2 3 4 |
func addOval() { layer.addSublayer(ovalLayer) ovalLayer.expand() } |
这段代码首先添加了你上面创建的 OverLayer 的实例作为一个子层到视图层,然后调用 expand() 方法,这是被切掉的需要你来填充的函数之一
来到 OvalLayer.swift 文件,添加如下代码到 expand() 中:
1 2 3 4 5 6 7 8 9 |
func expand() { var expandAnimation: CABasicAnimation = CABasicAnimation(keyPath: "path") expandAnimation.fromValue = ovalPathSmall.CGPath expandAnimation.toValue = ovalPathLarge.CGPath expandAnimation.duration = animationDuration expandAnimation.fillMode = kCAFillModeForwards expandAnimation.removedOnCompletion = false addAnimation(expandAnimation, forKey: nil) } |
这个函数创建了一个 CABasicAnimation 的实例,这个实例用于改变椭圆从 ovalPathLarge.到 ovalPathSmall 的路径。启动项目为你提供了两者的Bezier paths。
设置动画的 removedOnCompletion 的值为 false,fillMode 的值为 KCAFillModeForwards ,使得当动画结束的时候,椭圆保留它新的路径。
最后,打开 ViewController.swift ,在 view.addSubview(holderView) 下的 addHolderView() 方法中添加如下的线条
1 |
holderView.addOval() |
将 holdview 添加到 ViewController 的视图中后,调用 addOval 方法来启动动画
构建并运行你的应用,你的动画现在就会看起来像下面(图例)
摇动椭圆
使用视图中扩张的椭圆,下一步就是在椭圆的步调中设置一些反弹,使之摇摆起来
打开 HolderView.swift,在此类的底部,添加下面的函数
1 2 3 |
func wobbleOval() { ovalLayer.wobble() } |
在 OvalLayer 中调用被切掉的方法 wobble().
现在打开 OverLayer.swift,在 wobble() 中添加如下代码
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 |
func wobble() { // 1 var wobbleAnimation1: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation1.fromValue = ovalPathLarge.CGPath wobbleAnimation1.toValue = ovalPathSquishVertical.CGPath wobbleAnimation1.beginTime = 0.0 wobbleAnimation1.duration = animationDuration // 2 var wobbleAnimation2: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation2.fromValue = ovalPathSquishVertical.CGPath wobbleAnimation2.toValue = ovalPathSquishHorizontal.CGPath wobbleAnimation2.beginTime = wobbleAnimation1.beginTime + wobbleAnimation1.duration wobbleAnimation2.duration = animationDuration // 3 var wobbleAnimation3: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation3.fromValue = ovalPathSquishHorizontal.CGPath wobbleAnimation3.toValue = ovalPathSquishVertical.CGPath wobbleAnimation3.beginTime = wobbleAnimation2.beginTime + wobbleAnimation2.duration wobbleAnimation3.duration = animationDuration // 4 var wobbleAnimation4: CABasicAnimation = CABasicAnimation(keyPath: "path") wobbleAnimation4.fromValue = ovalPathSquishVertical.CGPath wobbleAnimation4.toValue = ovalPathLarge.CGPath wobbleAnimation4.beginTime = wobbleAnimation3.beginTime + wobbleAnimation3.duration wobbleAnimation4.duration = animationDuration // 5 var wobbleAnimationGroup: CAAnimationGroup = CAAnimationGroup() wobbleAnimationGroup.animations = [wobbleAnimation1, wobbleAnimation2, wobbleAnimation3, wobbleAnimation4] wobbleAnimationGroup.duration = wobbleAnimation4.beginTime + wobbleAnimation4.duration wobbleAnimationGroup.repeatCount = 2 addAnimation(wobbleAnimationGroup, forKey: nil) } |
代码真够多的。但断句还是很讲究的。 接下来要做的是:
1. 从大路径下降到被垂直压扁的动画
2. 从垂直压扁变成水平和垂直都压扁
3. 和垂直挤压(动画)切换
4. 回到大路径结束动画
5. 把你所有的动画合并到CAAnimationGroup组,并把这个动画组添加到你的 OvalLayout 中。
每一个随后的动画的 beginTime 都是其前一个动画和动画持续时间的 beginTime 总和。你重复动画组两次就会给你一种摆动出稍微拉长的感觉
尽管你现在拥有产生摇摆动画的所有代码,你还是不能调用你的新动画
我们回到 HolderView.swift,在 addOval() 结尾处添加如下代码
1 2 |
NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: "wobbleOval", userInfo: nil, repeats: false) |
在这里,你创建了一个timer定时器,它会在OvalLayer已经结束扩张后调用 wobbleOval()
编译并运行你的应用,检查下你的新动画。
这有点微妙,但那对一个真正的明快的动画是一个重要的因素。你不再需要那些满屏幕都是乱飞的东西了。
开始变身
是时候来电有趣的东西了。你将要把一个椭圆变身成为一个三角形。在用户眼里,这个转变应该看上去无缝连接的。要做到这些,你会用到两个相同颜色的分离的形状。
打开HolderView.swift,在HolderView类的顶端稍微靠近你早些时候添加的 OvalLayer 属性的下面添加如下代码
1 |
let triangleLayer = TriangleLayer() |
这里声明了一个 TriangleLayer 类的常量,正如你在 OvalLayer 中做的一样
现在,让wobbleOval()方法看上去像这样:
1 2 3 4 5 6 7 8 9 10 11 |
func wobbleOval() { // 1 layer.addSublayer(triangleLayer) // Add this line ovalLayer.wobble() // 2 // Add the code below NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: "drawAnimatedTriangle", userInfo: nil, repeats: false) } |
上面的代码做了如下这些事情:
- 这行(代码)添加了一个 TiangleLayer 实例,这个实例在稍早的时候作为HolderView层的子层已经被初始化过了。
- 正如你所知道的,因为这个摇摆动画在1.8s的总间隔时间内运行两次,所以在中间点启动变形过程会是一个非常好的地方。因此,你要添加一个定时器timer,它在延迟0.9s之后执行drawAnimatedTriangle()
注意:找到动画的正确的间隔或延迟需要反复实验,这也是一个好的动画和一个极好的动画区别。我鼓励你去修补你的动画,让它们看上去完美。这可能要花点时间,但确是值得的。
接下来,在此类的底部添加如下的函数。
1 2 3 |
func drawAnimatedTriangle() { triangleLayer.animate() } |
这个方法会被你刚刚加入到 wobbleOval() 中的timer定时器调用。
现在打开 TriangleLayer.swift,添加如下代码到 animate()