澳门新蒲京娱乐

图片 16
创建私有,创建私有Pods
图片 2
10中截取截图的6种方式,工作站上截图

实现Uber的启动动画,仿Uber启动动画

此篇为译文,若存在纰漏,请见谅。

这是一篇转载的译文,非常感谢译者的分享,原译文地址

原文:How To Create an Uber Splash Screen

可以在这下载到由本人所写的OC版实现代码,欢迎指正,欢迎Star

新蒲京官方下载 1

您还可以在这里找到由译者更新至Swift 3.0的最终的Fuber工程,请使用Xcode
8.0 beta4 或更新版本打开。

一个完美的启动动画—通过有趣的动画让开发者不会再为app启动时依赖API返回核心数据而产生的延时问题抓狂。有趣的启动动画(启动画面不再是静态的,无动画的启动画面)会在app中起到十分重要的作用:让用户有耐心等待app的启动。

-------------------

尽管我们能在很多app中见到启动动画,但是你很难找到一个比Uber漂亮的。在2016年的第一个季度,Uber推出了新版major
rebranding strare gy led by its
CEO。其中的一个变化就是带来了一个十分酷炫的启动画面。

本文翻译自 How To Create an Uber Splash Screen由 Derek Selander
发表于Raywenderlich

此篇教程的目的是尽可能地还原Uber的启动动画。其中重度使用 CALayer
CAAnimation
类,包括他们的子类。除了介绍这些这些类的概念,本教程会更注重如何使用这些类来构造高质量的动画。想要深入学习这些动画,看这里:Marin
Todorov’s Intermediate iOS Animation video series

受限于译者英语水平及翻译经验,译文难免有词不达意,甚至错误的地方,还望不吝赐教,予以指正

因为在教程中需要实现大量的动画,所以你将从一个初始工程开始学习,我们已经在这个工程中创建了所有与动画相关的CALayer类的实例。下载工程

-------------------

初始项目为一个名为Fuber的app。(译者注:接下来这段话是原文作者卖萌)Fuber提供呼叫Segway(一种独轮电动自行车)司机来接载乘客到城市中的任意一个角落。Fuber发展迅速,现在已经在60多个国家为Segway乘客服务,但是遭到了许多国家政府的反对就像Segway工会反对用户使用Fuber联系Segway司机。:]

一个好的溅落式启动页(别被毫无动画效果的静态启动页迷惑),使开发人员有机会在展示动画期间,从后端获取必要的数据。同时它在应用启动期间让用户始终保持高昂兴趣方面也发挥了重要作用。

新蒲京官方下载 2

虽然溅落式启动页已广泛存在,但是你很难找到一个如Uber这般出色的。在2016年的首季,Uber释出一个由CEO领导的品牌重塑战略,品牌重塑的成果之一,便是一个非常炫酷的溅落式启动页。

教程结束的时候,你会创建出一个如下图的启动动画:

本文以仿制Uber启动动画为目标。其中运用了大量的CAlayerCAAnimation类,及其相应子类。相对于概念介绍,本文更着重于如何运用这些类去实现一个产品级的动画效果。如需了解动画背后的相关知识,请访问
Marin Todorov 的系列视频教程:Intermediate iOS Animation

新蒲京官方下载 3

鉴于本文涉及的动画众多,这里提供一个已为后续动画创建好所有CALayer的起始工程。

打开并运行Fuber项目,看一看。

起始工程是一个叫做Fuber的应用,Fuber提供驾乘共享服务,乘客通过向Segway驾驶员发出请求,来邀请其搭载自己抵达城市的任何地方。Fuber发展迅速,已在60多个国家为用户提供服务,但也面临众多国家的反对和工会要求其必须与司机签订合同的问题。:]

从UIViewController角度,app启动 SplashViewController
从父视图控制器-RootContainerViewController:负责控制它的子视图控制器。SplashViewController负责循环启动动画直到app准备好加载。一般这段时间内会去请求API以获取app启动所必须的数据。值得一提的是,在这个简单的示例项目中,启动动画拥有自己的模块。

<center>新蒲京官方下载 4Splash
screen

这里有两个方法在 RootContainerViewController:
showSplashViewController() 和
ShowSplashViewControllerNoPing()
。此教程的大部分时间,你只需要调用
ShowSplashViewControllerNoPing(),它只会循环启动动画,这样你可以专注于在
SplashViewController中的动画,之后你再会调用
showSplashViewController()
用来模拟请求API的延迟并转场进入主视图控制器。

</center>

SplashViewController视图中包含两个subview,第一个subview是
TileGridView,它有一个名为“ripple
grid”的背景图,它包含了一个格子布局的子视图实例
TileView。另外一个subview由动画视图 ‘U’ icon组成,名为
AnimatedULogoView

最终,我们会创建一个如下的非常炫酷的溅落式启动页:

新蒲京官方下载 5

<center>新蒲京官方下载 6Fuber
Animation

AnimatedULogoView包含了4个CAShapeLayer:

</center>

  • circleLayer 表示“U”的圆形白色背景。
  • lineLayer 是一条直线从 circleLayer 的中心延伸到它的边缘。
  • squareLayercircleLayer 中心的正方形。
  • maskLayer
    当其他图层的边界改变时,在一个简单的动画中它被用来统一控制这些图层。

打开并运行起始工程,简单浏览一下工程结构。

组合起来,这些 CAShaperLayer 创建了Fuber的“U”。

首先从视图控制器开始,应用通过负责子视图切出任务的RootContainerViewController加载SplashViewController。父视图控制器从启动页开始运行,直至应用的所有准备工作全部完成。这期间应用会连接到后端,获取后续所需数据。需要指出的是,在这个简单的项目中启动页被设计成了一个独立的模块。

新蒲京官方下载 7

RootContainerViewController中已经实现好了两个方法:showSplashViewController()
showSplashViewControllerNoPing()。由于教程中大部分时间,都在调用showSplashViewControllerNoPing()方法,所以我们先将精力放在SplashViewController的子视图动画创建上,然后在通过showSplashViewController()模拟一个访问API的延迟效果,并随即跳转到主视图控制器。

现在你知道了这些图层是怎么组合起来的,是时候去写动画代码让
AnimatedULogoView动起来。

SplashViewController的视图包含两个子视图。
第一个子视图是用于构成波纹网格背景的TileGridview,它包含了一系列按网格排列的TileView实例。另一个子视图名为AnimatedULogoView,它构成了
U 字型的动画图标。

在实现这些动画的过程中,最好排除外界干扰专注于正在实现的动画,点开
AnimatedULogoView.swift。在 init 中,注释掉添加这些 sublayer
除了 circleLayer
的代码。当完成所有动画之后,便会取消这些注释。代码现在应该是这个样子:

<center>新蒲京官方下载 8Splash
Screen

override init(frame: CGRect) { super.init(frame: frame) circleLayer = generateCircleLayer() lineLayer = generateLineLayer() squareLayer = generateSquareLayer() maskLayer = generateMaskLayer() // layer.mask = maskLayer layer.addSublayer(circleLayer)// layer.addSublayer(lineLayer)// layer.addSublayer(squareLayer)}

</center>

找到 generateCircleLayer() 去看看这个圆是怎么创建的。它是用
UIBezierPath新蒲京官方下载 , 创建出来的 CAShapeLayer 图层。注意这一行代码:

AnimatedULogoView包含4个CAShapeLayer:

layer.path = UIBezierPath(arcCenter: CGPointZero, radius: radius/2, startAngle: -CGFloat, endAngle: CGFloat, clockwise: true).CGPath
  • circleLayer 用于实现字母 U 的白色背景
  • lineLayer 用于实现从circleLayer的中心到边缘的一条线段
  • squareLayer 用于实现位于circleLayer中心位置的方块
  • maskLayer
    用作视图遮罩,通过改变其bounds的动画效果,来将其它所有图层的动画效果整齐划一地混合起来。

默认情况下,也就是 startAngle
参数为0,贝尔塞曲线的路径会从右边开始。当设置为 -M_PI_2
也就是-90°,这个曲线会从圆的正上方开始绘制,因为 endAngle
参数设置为270°及
3M_PI_2*,曲线会在圆的正上方结束绘制。因为你要动画展示这个绘制圆的过程,所以圆的半径radius 与曲线的线宽 lineWidth** 相同。

通过组合这几个CAShaperLayer动画,共同实现了Fuber中字母 U
的动画效果。

circleLayer 的动画需要3个 CAAnimation组合起来:一个关键帧动画
CAkeyframeAnimation 绘制圆键值为
strokeEnd,一个转换基础关键帧动画 CABasicAnimation
使圆的形态转换,最后一个为动画组 CAAnimationGroup
用来将前面两个动画组合起来。接下来让我们创建它们。

<center>新蒲京官方下载 9RiderIconView

找到 animateCirleLayer() 添加以下代码:

</center>

 // strokeEnd let strokeEndAnimation = CAKeyframeAnimation(keyPath: "strokeEnd") strokeEndAnimation.timingFunction = strokeEndTimingFunction strokeEndAnimation.duration = kAnimationDuration - kAnimationDurationDelay strokeEndAnimation.values = [0.0, 1.0] strokeEndAnimation.keyTimes = [0.0, 1.0]

了解了图层的构成之后,接下来我们就来添加一些动画让AnimatedULogoView动起来吧。

通过设置这个动画的 values
[0.0,1.0],你会看到一个很cool的类似时钟的动画。当 strokeEnd
的值增加的时候,贝塞尔曲线的长度也跟着圆的周长增加,最后这个圆就被“填满”了。举个特定的例子,假如你将
values 的值设置为
[0.0,0.5],这个动画将只会绘制到一个半圆便结束了,因为 strokeEnd
停止在圆的周长一半的位置。(译者注:想要看到这一个小动画的效果,可以将这个动画加入
circleLayer
中,添加这一行代码:circleLayer.addAnimation(strokeEndAnimation, forKey:
“looping”) 后运行工程。)

创建复杂动画的关键,在于排除视觉干扰专注于我们正在实现的部分。
打开AnimatedULogoView.swift文件。找到init方法,注释掉除circleLayer外其它向视图中添加子图层的方法,完成动画后会再将其全部添加回来。注释完成后的代码如下:

现在来添加形态转换动画:

override init(frame: CGRect) { super.init(frame: frame) circleLayer = generateCircleLayer() lineLayer = generateLineLayer() squareLayer = generateSquareLayer() maskLayer = generateMaskLayer() // layer.mask = maskLayer layer.addSublayer(circleLayer)// layer.addSublayer(lineLayer)// layer.addSublayer(squareLayer)}
// transform let transformAnimation = CABasicAnimation(keyPath: "transform") transformAnimation.timingFunction = strokeEndTimingFunction transformAnimation.duration = kAnimationDuration - kAnimationDurationDelay var startingTransform = CATransform3DMakeRotation(-CGFloat, 0, 0, 1) startingTransform = CATransform3DScale(startingTransform, 0.25, 0.25, 1) transformAnimation.fromValue = NSValue(CATransform3D: startingTransform) transformAnimation.toValue = NSValue(CATransform3D: CATransform3DIdentity)

找到generateCircleLayer()方法,了解下圆形是如何被创建的。其实只是简单地通过
UIBezierPath 创建了一个 CAShapeLayer 。 注意看这行代码:

这个动画包含两个部分,一部分是比例变化,另一部分为z轴上旋转变化。这样
circleLayer
再绘制圆的过程中还会顺时针旋转45°。旋转动画十分重要,它需要配合
lineLayer 图层动画的位置与速度。

layer.path = UIBezierPath(arcCenter: CGPointZero, radius: radius/2, startAngle: -CGFloat, endAngle: CGFloat, clockwise: true).CGPath

最后,添加一个动画组
CAAnimationGroup,这个动画组包含了之前的两个动画,所以你只需要将这个动画组加入到
circleLayer图层即可。

startAngle 传入 0 或使用默认值, 弧线会从右侧开始。传入
-M_PI_2 即 -90度, 则会从顶部开始,如果 endAngle 恰好是270度即
3 *
M_PI_2
,弧线则再次回到顶点。注意为了绘制的动画效果,我们使用圆形的半径作为lineWidth

// Group let groupAnimation = CAAnimationGroup() groupAnimation.animations = [strokeEndAnimation, transformAnimation] groupAnimation.repeatCount = Float.infinity groupAnimation.duration = kAnimationDuration groupAnimation.beginTime = beginTime groupAnimation.timeOffset = startTimeOffset circleLayer.addAnimation(groupAnimation, forKey: "looping")

circleLayer的动画需要三个CAAnimation子类来实现:一个作用于stokeEndCAKeyframeAnimation动画,一个作用于transformCABasicAnimation动画,和一个负责将两部分动画组合起来的CAAnimationGroup。这将一次性同时创建所有动画。

这个动画组 CAAnimationGroup
有两个值得关注的属性被设置:beginTime
timeOffset。如果你对它们都不熟悉,这里有一篇很赞的文章介绍它们以及它们的用途。

在事先写好的animateCircleLayer()方法中添加如下代码:

这个动画组 groupAnimationbeginTime 设置参照于它的父视图。

 // strokeEnd let strokeEndAnimation = CAKeyframeAnimation(keyPath: "strokeEnd") strokeEndAnimation.timingFunction = strokeEndTimingFunction strokeEndAnimation.duration = kAnimationDuration - kAnimationDurationDelay strokeEndAnimation.values = [0.0, 1.0] strokeEndAnimation.keyTimes = [0.0, 1.0]

timeOffset是必须要设置的,因为这个动画不是从动画循环的起点开始的。当你完成了更多的动画之后,尝试去修改
startTimeOffset的值并观察动画发生了什么变化。(译者注:关于timeOffset可以这么理解,假如一段动画是一个环,持续时间为5秒,设置timeOffset的值为2秒,那么这个动画循环将从2秒开始到5秒,然后再从0秒到2秒,这样的一个流程)

通过向动画的Values属性提供的 0.0 和 1.0,我们便透过Core
Animation框架生成了一个从 startAngleendAngle
沿顺时针旋转的动画。随着 strokeEnd
属性值的增加,弧线沿着圆周慢慢伸展,圆形也渐渐被”填满”。在这个例子中,如果我们将values属性的值设为[0.0,
0.5],则仅会画半个圆,这是因为 StrokeEnd
在动画结束时刚达好到圆周的一半。

将这个动画组加到 circleLayer 图层后,运行工程,动画的效果应该如图:

译者注:“圆形也渐渐被‘填满’”一句的填满是引起来的,并不是真的被填满,而是描边的
lineWidth
与圆形半径相同,从而产生了填满的视觉效果。可参考generateCircleLayer()方法中layer.fillColor = UIColor.clear.cgColor这段代码,事实上填充色被设置为透明,

新蒲京官方下载 10

现在添加形变(transform)动画:

注意:尝试从 groupAnimation.animations 数组中移除
strokeEndAnimation 或者
transformAnimation,来看看每个动画究竟是什么样子的。尽量在本教程中对每一个你创建的动画采用这个方式来预览,你会惊讶于这些动画组合出了你意想不到的效果。

 // transform let transformAnimation = CABasicAnimation(keyPath: "transform") transformAnimation.timingFunction = strokeEndTimingFunction transformAnimation.duration = kAnimationDuration - kAnimationDurationDelay var startingTransform = CATransform3DMakeRotation(-CGFloat, 0, 0, 1) startingTransform = CATransform3DScale(startingTransform, 0.25, 0.25, 1) transformAnimation.fromValue = NSValue(CATransform3D: startingTransform) transformAnimation.toValue = NSValue(CATransform3D: CATransform3DIdentity)

已经完成了 circleLayer 动画,接下来我们来解决
lineLayer动画。还是在 AnimatedULogoView.swift,找到
startAnimating() 注释掉调用动画的代码除了
animateLineLayer()。代码看起来应该是如下的样子:

该动画同时实现了放大和沿 Z
轴旋转的两个形变。这使得圆形在沿顺时针旋转45度的同时逐渐变大。这里的旋转很重要,因为圆形的旋转要与lineLayer和其它图层一块动起来时的位置和速度保持一致。

public func startAnimating() { beginTime = CACurrentMediaTime() layer.anchorPoint = CGPointZero // animateMaskLayer()// animateCircleLayer() animateLineLayer()// animateSquareLayer()}

最后在animateCircleLayer()方法的最下面添加一个CAAnimationGroup。这个动画组将包含之前的两个动画,这样我们仅向circleLayer图层添加一次动画即可。

除此之外,改变 init 中的内容,这样我们只添加了 circleLayer
lineLayer

 // Group let groupAnimation = CAAnimationGroup() groupAnimation.animations = [strokeEndAnimation, transformAnimation] groupAnimation.repeatCount = Float.infinity groupAnimation.duration = kAnimationDuration groupAnimation.beginTime = beginTime groupAnimation.timeOffset = startTimeOffset circleLayer.addAnimation(groupAnimation, forKey: "looping")
override init(frame: CGRect) { super.init(frame: frame) circleLayer = generateCircleLayer() lineLayer = generateLineLayer() squareLayer = generateSquareLayer() maskLayer = generateMaskLayer() // layer.mask = maskLayer layer.addSublayer(circleLayer) layer.addSublayer(lineLayer)// layer.addSublayer(squareLayer)}

这里我们修改了CAAnimationGroup的两个重要属性:beginTime
timeOffset。如果你对其中任何一个不熟悉,那么你都可以在这里找到关于该属性的介绍和使用说明。将
groupAnimationbeginTime 设置为与父视图相同。

接下来找到 animateLineLayer() 在实现中添加下一组动画:

timeOffeset的设置是必要的,因为动画首次运行时实际上是从一半开始的。当完成更多动画效果后,你可以试着改变startTimeOffset的值,并观察动画在视觉效果上的不同。

// lineWidth let lineWidthAnimation = CAKeyframeAnimation(keyPath: "lineWidth") lineWidthAnimation.values = [0.0, 5.0, 0.0] lineWidthAnimation.timingFunctions = [strokeEndTimingFunction, circleLayerTimingFunction] lineWidthAnimation.duration = kAnimationDuration lineWidthAnimation.keyTimes = [0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]

将动画组添加到circleLayer之后,编译并运行应用,检查下动画效果.

这个动画用来控制直线线宽由细到粗再到细的过程。

<center>新蒲京官方下载 11Splash
Screen CircleIn Animation

下一个转换动画,添加:

</center>

 // transform let transformAnimation = CAKeyframeAnimation(keyPath: "transform") transformAnimation.timingFunctions = [strokeEndTimingFunction, circleLayerTimingFunction] transformAnimation.duration = kAnimationDuration transformAnimation.keyTimes = [0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0] var transform = CATransform3DMakeRotation(-CGFloat, 0.0, 0.0, 1.0) transform = CATransform3DScale(transform, 0.25, 0.25, 1.0) transformAnimation.values = [NSValue(CATransform3D: transform), NSValue(CATransform3D: CATransform3DIdentity), NSValue(CATransform3D: CATransform3DMakeScale(0.15, 0.15, 1.0))]

注意:
试着删除groupAnimation.animations数组中的strokeEndAnimationtransformAnimation,以确认每个动画具体实现了哪些视觉效果.
可以按该方法再去验证一下文中的其它动画,你会惊讶于,仅仅改变动画的组合方式就可以产生如此令人难以预料的独特视觉效果.

circleLayer
转换动画很像,在这里你定义一个绕着z轴顺时针旋转。对直线而言,首先执行25%的比例变换,紧接着变换成15%(百分比相对于直线原始尺寸而言)。

完成了circleLayer的动画, 接下来我们再来完成lineLayer动画。还是在
AnimatedULogoView.swift文件中,
找到startAnimating()方法并注释掉除animateLineLayer()外的所有动画调用。注释后的代码如下:

将上面的两个动画使用一个 CAAnimationGroup
组合起来,并将这个组合动画添加到 lineLayer:

public func startAnimating() { beginTime = CACurrentMediaTime() layer.anchorPoint = CGPointZero // animateMaskLayer()// animateCircleLayer() animateLineLayer()// animateSquareLayer()}
// Group let groupAnimation = CAAnimationGroup() groupAnimation.repeatCount = Float.infinity groupAnimation.removedOnCompletion = false groupAnimation.duration = kAnimationDuration groupAnimation.beginTime = beginTime groupAnimation.animations = [lineWidthAnimation, transformAnimation] groupAnimation.timeOffset = startTimeOffset lineLayer.addAnimation(groupAnimation, forKey: "looping")

此外,
修改init方法中的代码,只显示circleLayerlineLayer两个图层:

运行工程,看到这个prettiness的动画:

override init(frame: CGRect) { super.init(frame: frame) circleLayer = generateCircleLayer() lineLayer = generateLineLayer() squareLayer = generateSquareLayer() maskLayer = generateMaskLayer() // layer.mask = maskLayer layer.addSublayer(circleLayer) layer.addSublayer(lineLayer)// layer.addSublayer(squareLayer)}

新蒲京官方下载 12

注释掉图层和动画后, 转到animateLineLayer()方法并实现下面这组动画:

请注意你使用 -M_PI_4 初始转换值与画圆动画配合起来。你还需要设置
keyTimes 为 [0.0, 1.0 -kAnimationDurationDelay/kAnimationDuration,
1.0]。这个数组的第一个和最后一个元素的含义很明显:0表示开始,1.0表示结束,中间的元素需要去计算画圆完成的时间紧接着开始缩小动画。用
kAnimationDurationDelaykAnimationDuration
获得准确的百分比,因为这是个延时动画,所以需要用1.0减去这个百分比才是延时时间。

 // lineWidth let lineWidthAnimation = CAKeyframeAnimation(keyPath: "lineWidth") lineWidthAnimation.values = [0.0, 5.0, 0.0] lineWidthAnimation.timingFunctions = [strokeEndTimingFunction, circleLayerTimingFunction] lineWidthAnimation.duration = kAnimationDuration // Swift 3.0 keyTimes是一个NSNumber数组 lineWidthAnimation.keyTimes = [0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]

你现在已经完成了 circleLayerlineLayer
动画,是时候实现圆中心的方块动画。

该动画会使lineLayer的宽度呈现出先增后减的效果。

接下来你应该很熟悉了,找到 startAnimating() 注释掉调用动画的方法除了
animateSquareLayer()。除此之外,修改 init 如下:

再为接下来的动画添加如下代码:

override init(frame: CGRect) { super.init(frame: frame) circleLayer = generateCircleLayer() lineLayer = generateLineLayer() squareLayer = generateSquareLayer() maskLayer = generateMaskLayer() // layer.mask = maskLayer layer.addSublayer(circleLayer)// layer.addSublayer(lineLayer) layer.addSublayer(squareLayer)}
 // transform let transformAnimation = CAKeyframeAnimation(keyPath: "transform") transformAnimation.timingFunctions = [strokeEndTimingFunction, circleLayerTimingFunction] transformAnimation.duration = kAnimationDuration transformAnimation.keyTimes = [0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0] var transform = CATransform3DMakeRotation(-CGFloat, 0.0, 0.0, 1.0) transform = CATransform3DScale(transform, 0.25, 0.25, 1.0) transformAnimation.values = [NSValue(CATransform3D: transform), NSValue(CATransform3D: CATransform3DIdentity), NSValue(CATransform3D: CATransform3DMakeScale(0.15, 0.15, 1.0))]

完成之后,找到 animateSquareLayer() 然后开始实现下一个动画:

circleLayer的形变动画非常相似, 这里我们定义了个一个沿 Z
轴顺时针旋转的动画。
此外我们还为线段添加了一个先缩小到25%,再恢复到原有尺寸,最后再缩小到15%的形变动画.

 // bounds let b1 = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: 2.0/3.0 * squareLayerLength, height: 2.0/3.0 * squareLayerLength)) let b2 = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: squareLayerLength, height: squareLayerLength)) let b3 = NSValue(CGRect: CGRectZero) let boundsAnimation = CAKeyframeAnimation(keyPath: "bounds") boundsAnimation.values = [b1, b2, b3] boundsAnimation.timingFunctions = [fadeInSquareTimingFunction, squareLayerTimingFunction] boundsAnimation.duration = kAnimationDuration boundsAnimation.keyTimes = [0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]

通过CAAnimationGroup将动画组合起来,并添加到lineLayer上:

这个特别的动画改变了 CALayer
的边界。让这个方形的边长从3分之2的长度开始变化到原长最后变为0。

 // Group let groupAnimation = CAAnimationGroup() groupAnimation.repeatCount = Float.infinity groupAnimation.removedOnCompletion = false groupAnimation.duration = kAnimationDuration groupAnimation.beginTime = beginTime groupAnimation.animations = [lineWidthAnimation, transformAnimation] groupAnimation.timeOffset = startTimeOffset lineLayer.addAnimation(groupAnimation, forKey: "looping")

接下来,改变背景颜色的动画:

编译并运行,注意观察变化.

// backgroundColor let backgroundColorAnimation = CABasicAnimation(keyPath: "backgroundColor") backgroundColorAnimation.fromValue = UIColor.whiteColor().CGColor backgroundColorAnimation.toValue = UIColor.fuberBlue().CGColor backgroundColorAnimation.timingFunction = squareLayerTimingFunction backgroundColorAnimation.fillMode = kCAFillModeBoth backgroundColorAnimation.beginTime = kAnimationDurationDelay * 2.0 / kAnimationDuration backgroundColorAnimation.duration = kAnimationDuration / (kAnimationDuration - kAnimationDurationDelay)

<center>新蒲京官方下载 13Splash
Screen Knockoutline Animation

注意 fillMode 属性,因为 beginTime
不为0,这个动画会固定住开始与结束的颜色,这样添加这个动画进入动画组的时候就不会出现闪烁。

</center>

说到这,是时候实现这个动画组了:

注意我们设置了相同的初始形变值-M_PI_4,以便线段和圆形在绘制时能对齐。为此我们还将keyTimes
设置为[0.0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]
数组中首尾两个元素是确定的: 0 代表动画开始那一刻,1.0
代表动画结束那一刻,然后通过计算来获取圆形绘制刚刚结束、第二部分的动画即将开始时的那一刻。由于它是一个延迟的动画效果,所以我们还需要从
1.0
中减去通过kAnimationDurationDelay除以kAnimationDuration而得到的确切百分比,这是因为我们想让动画在结束后的延迟过程中再返回到起点。(译者:形成一个循环动画,否则会出现跳跃,致使动画不连贯)

 // Group let groupAnimation = CAAnimationGroup() groupAnimation.animations = [boundsAnimation, backgroundColorAnimation] groupAnimation.repeatCount = Float.infinity groupAnimation.duration = kAnimationDuration groupAnimation.removedOnCompletion = false groupAnimation.beginTime = beginTime groupAnimation.timeOffset = startTimeOffset squareLayer.addAnimation(groupAnimation, forKey: "looping")

circleLayerlineLayer动画都已完成,接下来我们该完成中间的方块动画了。

运行工程,你现在可以看到如下方块动画的效果:

与之前类似。
startAnimating()函数中注释掉除animateSquareLayer外的其它动画方法调用。然后在像下面这样修改init方法的代码:

新蒲京官方下载 14

override init(frame: CGRect) { super.init(frame: frame) circleLayer = generateCircleLayer() lineLayer = generateLineLayer() squareLayer = generateSquareLayer() maskLayer = generateMaskLayer() // layer.mask = maskLayer layer.addSublayer(circleLayer)// layer.addSublayer(lineLayer) layer.addSublayer(squareLayer)}

是时候组合以上实现的动画,看看这些动画组合起来的效果吧!

完成后转到animateSquareLayer()方法实现如下动画代码:

注意:这些动画在模拟器上显示可能会出现锯齿状边缘,因为是电脑模拟iOS设备的GPU。如果电脑无法实现这些动画效果,尝试切换到一个更小屏幕尺寸的模拟器或者在真机上运行程序。

 // bounds let b1 = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: 2.0/3.0 * squareLayerLength, height: 2.0/3.0 * squareLayerLength)) let b2 = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: squareLayerLength, height: squareLayerLength)) let b3 = NSValue(CGRect: CGRectZero) let boundsAnimation = CAKeyframeAnimation(keyPath: "bounds") boundsAnimation.values = [b1, b2, b3] boundsAnimation.timingFunctions = [fadeInSquareTimingFunction, squareLayerTimingFunction] boundsAnimation.duration = kAnimationDuration boundsAnimation.keyTimes = [0, 1.0-kAnimationDurationDelay/kAnimationDuration, 1.0]

首先,取消 init 以及 starAnimating() 中被注释的代码。

这一部分动画用于改变CALayer的大小。创建一个先将其边长缩小到2/3,再恢复,最终在缩小到零的关键帧动画。

所有的动画都被添加之后,运行工程:

接下来,为背景色添加动画效果:

新蒲京官方下载 15看起来还是差一点,是吧?有一个突然的闪烁当
circleLayer
的边界缩小的时候。幸运的是,mask动画可以去掉这个闪烁,让边界的收缩更加平滑。

 // backgroundColor let backgroundColorAnimation = CABasicAnimation(keyPath: "backgroundColor") backgroundColorAnimation.fromValue = UIColor.whiteColor().CGColor backgroundColorAnimation.toValue = UIColor.fuberBlue().CGColor backgroundColorAnimation.timingFunction = squareLayerTimingFunction backgroundColorAnimation.fillMode = kCAFillModeBoth backgroundColorAnimation.beginTime = kAnimationDurationDelay * 2.0 / kAnimationDuration backgroundColorAnimation.duration = kAnimationDuration / (kAnimationDuration - kAnimationDurationDelay)

找到 animateMaskLayer() 添加以下代码:

注意上面的fillMode属性。一旦beginTime不为零时,
动画就会在起始点和结束点保持住当前颜色。这避免了动画在被添加到父CAAnimationGroup时出现闪烁。(译者:这里译的不好:(。请试着改变该属性的设置,看看视觉效果上有什么不同,以加深理解。)

// bounds let boundsAnimation = CABasicAnimation(keyPath: "bounds") boundsAnimation.fromValue = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: radius * 2.0, height: radius * 2)) boundsAnimation.toValue = NSValue(CGRect: CGRect(x: 0.0, y: 0.0, width: 2.0/3.0 * squareLayerLength, height: 2.0/3.0 * squareLayerLength)) boundsAnimation.duration = kAnimationDurationDelay boundsAnimation.beginTime = kAnimationDuration - kAnimationDurationDelay boundsAnimation.timingFunction = circleLayerTimingFunction

了解了这些,我们就动手来实现一下吧:

这个是改变边界的动画。请记住当 maskLayer 的边界改变的时候,整个
AnimateULogoView 都会消失,因为 maskLayer 是最底层的图层。

 // Group let groupAnimation = CAAnimationGroup() groupAnimation.animations = [boundsAnimation, backgroundColorAnimation] groupAnimation.repeatCount = Float.infinity groupAnimation.duration = kAnimationDuration groupAnimation.removedOnCompletion = false groupAnimation.beginTime = beginTime groupAnimation.timeOffset = startTimeOffset squareLayer.addAnimation(groupAnimation, forKey: "looping")

现在来实现一个 cornerRadius 动画,来保持 maskLayer
边界是一个圆:

编译并运行检查动画效果。注意观察方块的变化。

 // cornerRadius let cornerRadiusAnimation = CABasicAnimation(keyPath: "cornerRadius") cornerRadiusAnimation.beginTime = kAnimationDuration - kAnimationDurationDelay cornerRadiusAnimation.duration = kAnimationDurationDelay cornerRadiusAnimation.fromValue = radius cornerRadiusAnimation.toValue = 2 cornerRadiusAnimation.timingFunction = circleLayerTimingFunction

<center>新蒲京官方下载 16Splash
Screen Tutorial

将这两个动画加入动画组中,并将动画组添加到这个图层:

</center>

 // Group let groupAnimation = CAAnimationGroup() groupAnimation.removedOnCompletion = false groupAnimation.fillMode = kCAFillModeBoth groupAnimation.beginTime = beginTime groupAnimation.repeatCount = Float.infinity groupAnimation.duration = kAnimationDuration groupAnimation.animations = [boundsAnimation, cornerRadiusAnimation] groupAnimation.timeOffset = startTimeOffset maskLayer.addAnimation(groupAnimation, forKey: "looping")

现在将所有的动画组合起来看看效果如何!

运行工程:

注意:
在电脑的GPU完成对iOS设备的模拟任务前,模拟器上的动画可能会有那么一点小抽。如果你的电脑带不动动画,可以试着将模拟器窗口调小或者转到真机开发。

相关文章

No Comments, Be The First!
近期评论
    功能
    网站地图xml地图