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
funcwobble(){
wobbleAnimation1:CABasicAnimation=CABasicAnimation(keyPath:
)
wobbleAnimation1.fromValue=ovalPathLarge.CGPath
wobbleAnimation1.toValue=ovalPathSquishVertical.CGPath
wobbleAnimation1.beginTime=0.0
wobbleAnimation1.duration=animationDuration
//2
wobbleAnimation2:CABasicAnimation=CABasicAnimation(keyPath:
)
wobbleAnimation2.fromValue=ovalPathSquishVertical.CGPath
wobbleAnimation2.toValue=ovalPathSquishHorizontal.CGPath
wobbleAnimation2.beginTime=wobbleAnimation1.beginTime+wobbleAnimation1.duration
wobbleAnimation2.duration=animationDuration
//3
wobbleAnimation3:CABasicAnimation=CABasicAnimation(keyPath:
)
wobbleAnimation3.fromValue=ovalPathSquishHorizontal.CGPath
wobbleAnimation3.toValue=ovalPathSquishVertical.CGPath
wobbleAnimation3.beginTime=wobbleAnimation2.beginTime+wobbleAnimation2.duration
wobbleAnimation3.duration=animationDuration
//4
wobbleAnimation4:CABasicAnimation=CABasicAnimation(keyPath:
)
wobbleAnimation4.fromValue=ovalPathSquishVertical.CGPath
wobbleAnimation4.toValue=ovalPathLarge.CGPath
wobbleAnimation4.beginTime=wobbleAnimation3.beginTime+wobbleAnimation3.duration
wobbleAnimation4.duration=animationDuration
//5
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() 结尾处添加如下代码
2
NSTimer.scheduledTimerWithTimeInterval(0.3,target:self,selector:
"wobbleOval"
,
userInfo:nil,repeats:
false
)
|
在这里,你创建了一个timer定时器,它会在OvalLayer已经结束扩张后调用 wobbleOval()
编译并运行你的应用,检查下你的新动画。
这有点微妙,但那对一个真正的明快的动画是一个重要的因素。你不再需要那些满屏幕都是乱飞的东西了。
开始变身
是时候来电有趣的东西了。你将要把一个椭圆变身成为一个三角形。在用户眼里,这个转变应该看上去无缝连接的。要做到这些,你会用到两个相同颜色的分离的形状。
打开HolderView.swift,在HolderView类的顶端稍微靠近你早些时候添加的 OvalLayer 属性的下面添加如下代码
lettriangleLayer=TriangleLayer()
这里声明了一个 TriangleLayer 类的常量,正如你在 OvalLayer 中做的一样
现在,让wobbleOval()方法看上去像这样:
11
layer.addSublayer(triangleLayer)
ovalLayer.wobble()
//2
//Addthecodebelow
NSTimer.scheduledTimerWithTimeInterval(0.9,
selector:
"drawAnimatedTriangle"
repeats:
)
上面的代码做了如下这些事情:
-
这行(代码)添加了一个 TiangleLayer 实例,这个实例在稍早的时候作为HolderView层的子层已经被初始化过了。
-
正如你所知道的,因为这个摇摆动画在1.8s的总间隔时间内运行两次,所以在中间点启动变形过程会是一个非常好的地方。因此,你要添加一个定时器timer,它在延迟0.9s之后执行drawAnimatedTriangle()
注意:找到动画的正确的间隔或延迟需要反复实验,这也是一个好的动画和一个极好的动画区别。我鼓励你去修补你的动画,让它们看上去完美。这可能要花点时间,但确是值得的。
接下来,在此类的底部添加如下的函数。
funcdrawAnimatedTriangle(){
triangleLayer.animate()
这个方法会被你刚刚加入到 wobbleOval() 中的timer定时器调用。
现在打开 TriangleLayer.swift,添加如下代码到 animate()
27
funcanimate(){
triangleAnimationLeft:CABasicAnimation=CABasicAnimation(keyPath:
triangleAnimationLeft.fromValue=trianglePathSmall.CGPath
triangleAnimationLeft.toValue=trianglePathLeftExtension.CGPath
triangleAnimationLeft.beginTime=0.0
triangleAnimationLeft.duration=0.3
triangleAnimationRight:CABasicAnimation=CABasicAnimation(keyPath:
)
triangleAnimationRight.fromValue=trianglePathLeftExtension.CGPath
triangleAnimationRight.toValue=trianglePathRightExtension.CGPath
triangleAnimationRight.beginTime=triangleAnimationLeft.beginTime+triangleAnimationLeft.duration
triangleAnimationRight.duration=0.25
triangleAnimationTop:CABasicAnimation=CABasicAnimation(keyPath:
)
triangleAnimationTop.fromValue=trianglePathRightExtension.CGPath
triangleAnimationTop.toValue=trianglePathTopExtension.CGPath
triangleAnimationTop.beginTime=triangleAnimationRight.beginTime+triangleAnimationRight.duration
triangleAnimationTop.duration=0.20
triangleAnimationGroup:CAAnimationGroup=CAAnimationGroup()
triangleAnimationGroup.animations=[triangleAnimationLeft,triangleAnimationRight,
triangleAnimationTop]
triangleAnimationGroup.duration=triangleAnimationTop.beginTime+triangleAnimationTop.duration
triangleAnimationGroup.fillMode=kCAFillModeForwards
triangleAnimationGroup.removedOnCompletion=
false
addAnimation(triangleAnimationGroup,forKey:nil)
这段代码使三角层TriangleLayer的角一个挨一个的被弹拉成为椭圆 OvalLayer 层的摆动。Bezier path已经作为启动工程的一部分被定义好。左边的角首先执行,接下来是右边的角,最后是上面的。你完成这个(动画)需要借助创建三个基于路径的CABasicAnimation类的实例, CABasicAnimation 类已经被你添加到 CAAnimationGroup 组中,而组则被放到了 TriangleLayer 中。
构建并运行你的应用,看看当前动画的状态.
完成变形
为了完成变形过程,你需要在缩小OvalLayer椭圆层的同时,对 HolderView 旋转360度,让 TriangleLayer 三角层单独隔离出来。
添加如下代码
"spinAndTransform"
这里设置了一个定时器timer,用于在三角形动画结束后触发。0.9s的时间还次用反复实验来确定
现在在这个类的底部添加如下的函数。
14
funcspinAndTransform(){
layer.anchorPoint=CGPointMake(0.5,0.6)
//2
rotationAnimation:CABasicAnimation=CABasicAnimation(keyPath:
"transform.rotation.z"
)
rotationAnimation.toValue=CGFloat(M_PI*2.0)
rotationAnimation.duration=0.45
rotationAnimation.removedOnCompletion=
true
layer.addAnimation(rotationAnimation,forKey:nil)
//3
ovalLayer.contract()
你之前创建的定时器添加了这段代码,定时器会在椭圆停止摆动并且三角行的角出现的时候调用这个函数。在这里我们看下这个函数更详细的(介绍)
-
更新层的锚点到略微靠近视图中间的下方。这提供了一个看上去更加自然的旋转。这是由于椭圆和三角形事实上比视图中心在垂直方向上略微偏移。因此,如果视图围绕中心旋转,椭圆和三角形可能会垂直方向移动
-
应用一个CABasicAnimation类来对层做360度旋转,或者2*pi的弧度。旋转是围绕着Z轴,Z轴就是穿过屏幕,垂直于屏幕平面的轴
-
在OvalLayer中调用contract()来展示动画,这个动画会削减椭圆的尺寸直到消失
现在打开 OvalLayer.swift,添加如下代码到 contract() 方法
funccontract(){
contractAnimation:CABasicAnimation=CABasicAnimation(keyPath:
contractAnimation.fromValue=ovalPathLarge.CGPath
contractAnimation.toValue=ovalPathSmall.CGPath
contractAnimation.duration=animationDuration
contractAnimation.fillMode=kCAFillModeForwards
contractAnimation.removedOnCompletion=
addAnimation(contractAnimation,serif; font-size:14px; line-height:28px"> 这段代码应用 CABasicAnimation 类,将 OvalLayer 设置它的初始路径 ovalPathSmall。
构建并运行你的应用程序,当动画完成的时候,只有三角形应该被留在屏幕上。
绘制容器
在下面这部分,你将要绘画一个矩形容器,用于创建一个闭合圈。你将会用到 RectangleLayer 的描边属性。你需要这样做两次,将红色和蓝色都作为描边色。
属性的下面
letredRectangleLayer=RectangleLayer()letblueRectangleLayer=RectangleLayer()
接下来添加如下代码到 spinAndTransform(): 的尾部。
6
NSTimer.scheduledTimerWithTimeInterval(0.45,monospace!important; font-size:1em!important; min-height:inherit!important; color:blue!important">"drawRedAnimatedRectangle"
)
NSTimer.scheduledTimerWithTimeInterval(0.65,
"drawBlueAnimatedRectangle"
这里创建两个定时器timer分别调用 drawRedAnimatedRectangle() 和 drawBlueAnimatedRectangle() 。旋转动画结束后,首先需要画出矩形,当红色矩形描边绘画接近完成的时候,蓝色矩形描边开始。
添加下面两个方法头此类的底部
funcdrawRedAnimatedRectangle(){
layer.addSublayer(redRectangleLayer)
redRectangleLayer.animateStrokeWithColor(Colors.red)
}
funcdrawBlueAnimatedRectangle(){
layer.addSublayer(blueRectangleLayer)
blueRectangleLayer.animateStrokeWithColor(Colors.blue)
一旦你添加矩形层 RectangleLayer 作为 HolderView 的子层,你就要调用 animateStrokeWithColor(color:) 并通过适当的颜色来绘画出边线。
现在打开 RectangleLayer.swift,像下面这样填充 animateStrokeWithColor(color:)
8
funcanimateStrokeWithColor(color:UIColor){
strokeColor=color.CGColor
strokeAnimation:CABasicAnimation=CABasicAnimation(keyPath:
"strokeEnd"
strokeAnimation.fromValue=0.0
strokeAnimation.toValue=1.0
strokeAnimation.duration=0.4
addAnimation(strokeAnimation,forKey:nil)
这段代码通过添加一个 CABasicAnimation对象,在 RectangleLayer 矩形层周围绘画了一个描边。CAShapeLayer 的 strokeEnd 的 key(也就是keyPath)指示了在路径周围多远的距离停止描边。通过将这个属性值从0调到1,你会产生一种路径被从开始到结束都被绘画的错觉。 而从1到0,将会产生整个路径被抹去的错觉。
编译并运行你的应用,查看两个描边是如何看起来像他们构建的容器的。
填充容器
动画的下一步就是填充容器。你要寻找到的效果就像是水填充到玻璃杯中。这是个非常棒的视觉特效,使之为一个大的飞溅特效
属性稍靠下添加如下的常量
现在在drawBlueAnimatedRectangle():尾部添加如下的代码
NSTimer.scheduledTimerWithTimeInterval(0.40,monospace!important; font-size:1em!important; min-height:inherit!important; color:blue!important">"drawArc"
这(段代码)创建了一个定时器,用于当蓝色 RectangleLayer 完成绘画后调用 drawArc()
在类的结尾添加如下的函数
funcdrawArc(){
layer.addSublayer(arcLayer)
arcLayer.animate()
这段代码是在你动画填充之前,添加了上面已经创建ArcLayer 的实例对象到HolderView 层。
打开ArcLayer.swift 然后添加如下代码到animate():
37
38
39
arcAnimationPre:CABasicAnimation=CABasicAnimation(keyPath:
arcAnimationPre.fromValue=arcPathPre.CGPath
arcAnimationPre.toValue=arcPathStarting.CGPath
arcAnimationPre.beginTime=0.0
arcAnimationPre.duration=animationDuration
arcAnimationLow:CABasicAnimation=CABasicAnimation(keyPath:
arcAnimationLow.fromValue=arcPathStarting.CGPath
arcAnimationLow.toValue=arcPathLow.CGPath
arcAnimationLow.beginTime=arcAnimationPre.beginTime+arcAnimationPre.duration
arcAnimationLow.duration=animationDuration
arcAnimationMid:CABasicAnimation=CABasicAnimation(keyPath:
arcAnimationMid.fromValue=arcPathLow.CGPath
arcAnimationMid.toValue=arcPathMid.CGPath
arcAnimationMid.beginTime=arcAnimationLow.beginTime+arcAnimationLow.duration
arcAnimationMid.duration=animationDuration
arcAnimationHigh:CABasicAnimation=CABasicAnimation(keyPath:
)
arcAnimationHigh.fromValue=arcPathMid.CGPath
arcAnimationHigh.toValue=arcPathHigh.CGPath
arcAnimationHigh.beginTime=arcAnimationMid.beginTime+arcAnimationMid.duration
arcAnimationHigh.duration=animationDuration
arcAnimationComplete:CABasicAnimation=CABasicAnimation(keyPath:
)
arcAnimationComplete.fromValue=arcPathHigh.CGPath
arcAnimationComplete.toValue=arcPathComplete.CGPath
arcAnimationComplete.beginTime=arcAnimationHigh.beginTime+arcAnimationHigh.duration
arcAnimationComplete.duration=animationDuration
arcAnimationGroup:CAAnimationGroup=CAAnimationGroup()
arcAnimationGroup.animations=[arcAnimationPre,arcAnimationLow,arcAnimationMid,
arcAnimationHigh,arcAnimationComplete]
arcAnimationGroup.duration=arcAnimationComplete.beginTime+arcAnimationComplete.duration
arcAnimationGroup.fillMode=kCAFillModeForwards
arcAnimationGroup.removedOnCompletion=
false
addAnimation(arcAnimationGroup,forKey:nil)
这个动画和之前的摇摆动画很相似。你创建了一个 CAAnimationGroup 动画组,动画组中包含五个基于路径的 CABasicAnimation 实例对象。
每个路径因高度递增而有了稍微不同的弧,这些路径也是启动项目的一部分。最后,将 CAAnimationGroup 动画组应用到层中,并使得动画组在完成的时候不会被移除,因而当动画完成的时候,它依然保留了自己的状态。
构建并运行你的应用,看看这个神奇的展开吧。
完成动画
剩下要做的就是扩展蓝色的HolderView视图来填充整个屏幕,并且添加一个UILabel作为一个logo添加到视图中
打开 HolderView.swift,在drawArc() 的结尾添加如下代码
NSTimer.scheduledTimerWithTimeInterval(0.90,monospace!important; font-size:1em!important; min-height:inherit!important; color:blue!important">"expandView"
这(段代码)创建了一个定时器,用于在 ArcLayer 填充到容器后调用 expandView()
现在,添加下面的函数到同一个类的底部:
21
funcexpandView(){
backgroundColor=Colors.blue
frame=CGRectMake(frame.origin.x-blueRectangleLayer.lineWidth,
frame.origin.y-blueRectangleLayer.lineWidth,
frame.size.width+blueRectangleLayer.lineWidth*2,monospace!important; font-size:1em!important; min-height:inherit!important">frame.size.height+blueRectangleLayer.lineWidth*2)
//3
layer.sublayers=nil
//4
UIView.animateWithDuration(0.3,delay:0.0,options:UIViewAnimationOptions.CurveEaseInOut,
animations:{
self.frame=self.parentFrame
},completion:{finished
in
self.addLabel()
})
代码分析
-
HolderView视图的背景设置为蓝色,和你填充到矩形的颜色匹配
-
帧扩展到你稍早时候添加的RectangleLayer矩形层的描边宽度,
-
所有的子层都移除。现在没有了椭圆,没有了三角形,没有了矩形图层
-
添加动画,并扩张HolderView填充屏幕,当动画结束的时候,调用addLabel().
在类的底部,添加如下函数
funcaddLabel(){
delegate?.animateLabel()
这里只是简单的调用视图的代理函数,展示label标签。
现在打开ViewController.swift,添加如下代码到animateLabel():
22
funcanimateLabel(){
holderView.removeFromSuperview()
view.backgroundColor=Colors.blue
label:UILabel=UILabel(frame:view.frame)
label.textColor=Colors.white
label.font=UIFont(name:
"HelveticaNeue-Thin"
label.textAlignment=NSTextAlignment.Center
label.text=
"S"
label.transform=CGAffineTransformScale(label.transform,0.25,0.25)
view.addSubview(label)
//3
UIView.animateWithDuration(0.4,usingSpringWithDamping:0.7,initialSpringVelocity:0.1,
animations:({
}),153)!important">in
self.addButton()
})
依次带入各个注释段
-
从视图中移除HolderView ,并设置视图的背景颜色为蓝色。
-
创建一个文本为"S"的UIlabel标签对象,用于展示logo,并添加到视图。
-
标签对象使用一个弹性动画来使之伸缩。一旦动画结束,调用 addButton() 来添加一个按钮到视图中,当按钮按下的时候,重复动画。
构建并运行应用程序,给自己点个赞,花个时间来欣赏自己构建的动画吧。
下一步
你可以从这里下载最终完整的项目。
这个指导书包含了相当多的不一样的动画技术,当这些动画都堆叠在一起的时候,能够创造一个相当复杂的加载动画,这确实能够让你的应用在第一次被(用户)运行的时候就眼前一亮。
从这里,放松自由的玩玩不一样的(动画 的)定时和形状,看看你能组装成哪些很酷的动画
如果你想让你新发现的动画技术提升一个档次,那我建议你看下我们的(这本)书iOS Animations by Tutorials.
我希望通过这本指导书你能得到极大的乐趣,并且,如果你有任何问题或者建议,请加入我们下面的论坛讨论吧 原文链接:https://www.f2er.com/swift/326020.html
| | | | | | | | |