如何用swift 旋转动画创建一个复杂的加载动画

使用CAReplicatorLayer创建动画 - 简书
下载简书移动应用
写了15728字,被140人关注,获得了139个喜欢
使用CAReplicatorLayer创建动画
在网上看到了这篇文章,写得很好,于是便把它翻译出来,这是我第一次翻译,翻得不好请轻喷,毕竟6级都没过的孩子,苦逼啊。原文链接:
这是2015年3月的iOS动画,这个教程将引导您通过使用CAReplicatorLayer创建很酷的动画。你可能从来没有听说过CAReplicatorLayer,那是一个很酷难以理解但强大的CoreAnimation类。我想借此机会感谢大家谁不停地传播这个词iOS Animations by Emails ,并帮助这最后的一个月中我们的动画界近1,000强劲增长。非常感谢所有谁Tweet宣传我的书的iOS动画教程所 -你们是最棒的!我希望,当你通过这个月的教程中看到你会很高兴,我把我的甜蜜的时间来准备这些:]享受!
——- 马林托多罗夫
教程:创建动画与CAReplicatorLayer
-本教程是专为Xcode的6.1.1或兼容的版本。
当我正在iOS的动画教程由 CAReplicatorLayer在我的头脑和我的主题列表了。然而,当我们的计划书,我们意识到并非所有的图层将使其在和CAReplicatorLayer被留下(他一生的故事!)。然而,这一个月的通讯是所有关于CAReplicatorLayer,我敢肯定,这将让我的技术编辑这本书富特顿很高兴。在本教程中我将指导您完成创建3个不同的电源动画使用CAReplicatorLayer。我们将从模仿内置在iOS的音乐中得音量振动条(原文“volume bars”)来开始这个教程。
第二步你将继续创建一个自定义的活动指示灯,最后您将创建一个动画来呈现在屏幕上的标志在一个不寻常的方式:
1.基本复制动画
CAReplicatorLayer是一个容器层-你添加内容到其中让复制图层复制其中内容。如果你把一个单一的形状-在复制层将显示在屏幕上几种形状。很酷的是你可以预先设定复制多少几何图形并且设定副本之间的距离,透明度或颜色都可以发生变化。因此您可以创建很酷的动画效果。在本教程的第一部分,你会从iOS的音乐应用程序山寨动画:
这个动画设有一个上下移动的红色矩形。副本之间存在位置和时间偏移。让我们开始吧!
创建一个新的Xcode项目,并选择单一视图模板(Single View template)。保存项目后打开ViewController.swift。在viewDidLoad中()添加以下代码 :
func animation1() {
并添加了一个空方法在ViewController:
func animation1() {
让我们开始创建复制层,添加方法 animation1():
let r = CAReplicatorLayer()
r.bounds = CGRect(x: 0.0, y: 0.0, width: 60.0, height: 60.0)
r.position = view.center
r.backgroundColor = UIColor.lightGrayColor().CGColor
view.layer.addSublayer(r)
这段代码创建了一个CAReplicatorLayer的实例并且设置其边界和位置。为了确定它在哪儿,我们得给它一个浅灰色的背景颜色,然后将其添加到视图控制器的视图层。现在运行应用程序,你会看到屏幕出现一条浅灰色的方形:
现在,让我们创建的第一个矩形(也可以说成是原始的矩形),在animation1()中添加代码
let bar = CALayer()
bar.bounds = CGRect(x: 0.0, y: 0.0, width: 8.0, height: 40.0)
bar.position = CGPoint(x: 10.0, y: 75.0)
bar.cornerRadius = 2.0
bar.backgroundColor = UIColor.redColor().CGColor
r.addSublayer(bar)
这段代码创建一个红色的圆角矩形,并将其定位在复制层的左侧。运行应用程序在看效果:
红色的圆角矩形出现在复制层外面,因为我们会让他上下移动。该动画的起始位置是复制界限之下 - 这就是为什么矩形似乎有点“偏”。说到动画,接下来添加动画代码如下:
let move = CABasicAnimation(keyPath: "position.y")
move.toValue = bar.position.y - 35.0
move.duration = 0.5
move.autoreverses = true
move.repeatCount = Float.infinity
bar.addAnimation(move, forKey: nil)
这将使红条反复的上下移动,这是一个很好的开始,虽然它看起来并不令人印象深刻,现在你是在事实上几乎已经准备好与你最终的动画!接着来复制!添加以下代码:
r.instanceCount = 3
这告诉你想在屏幕上现实内容的3份拷贝(包括原来的)。如果你运行应用程序,当让,现在你将不会看到任何变化,因为所有的3份拷贝都出现在相同的位置,并在同一时间进行同一动画。为了到达效果我们给每个拷贝加上向右的偏移:
r.instanceTransform = CATransform3DMakeTranslation(20.0,0.0,0.0)
这段代码告诉复制图层执行怎样的变换到内容。您可以设置instanceTransform的值为偏移20每一个,当您运行的应用程序,你应该可以看到3红柱彼此相邻:
原来的红色条和两个克隆都动画都在上下往复运动,酷!最后一步,以实现期望的动画效果是给每个条一比特延迟,使它们不会在一致地移动。加入一个代码最后一行:
r.instanceDelay = 0.33
instanceDelay是时间偏移复制图层渲染。动画将分别会有0.33s延迟0.66s的延迟。运行应用程序并检查结果 - 你应该看到矩形跳来跳去,就像在原来的动画。最后,你需要做两个快速的变化:
让红色矩形只显示在灰色矩形里面的内容;
r.masksToBounds = true
删除backgroundColor
如果你想不同的动画就去改变instanceCount,instanceTransform和instanceDelay。很酷,不是吗?
2.活动指示灯
记者我们来做更复杂的复制动画!切换到到viewDidLoad方法中()替换animation1():
animation2()
正如你想得一样,下一个步骤就是添加一个空的方法:
func animation2(){
在这部分教程你要创建活动指示器。为了好玩,我们将创建一个比内置的iOS的活动指示器更精细的动画。首先添加一个复制图层到视图控制器,加入到animation2() :
let R = CAReplicatorLayer()
r.bounds = CGRect(0.0,0.0,200.0,200.0)
r.cornerRadius = 10.0
r.backgroundColor = UIColor(white:0.0,alpha:0.75).CGColor
r.position = view.center
view.layer.addSublayer(r)
然后以完全相同的方式添加一个空的复制层灰色背景。这会让我们背景颜色来模拟一个HUD的活动。接下来添加绘制一个白色矩形屏幕上的一个简单的一层:
let dot = CALayer()
dot.bounds = CGRect(0.0,0.0,14.0,14.0)
dot.position = CGPoint(100.0,40.0)
dot.backgroundColor = UIColor(whilte:0.8,alpha:1.0).CGColor
dot.borderColor =UIColor(white:1.0,alapha:1.0).CGColor
dot.borderWidth = 1.0
dot.cornerRadius = 2.0
r.addSublayer(dot)
创建一个14×14的矩形,并给它一个2磅圆角半径。在最后你添加dot层的复制。现在运行应用程序,看看一切看起来就像至今:
现在复制15个点来组成一个圈,每次旋转的角度等于2π/ 15:
let nrDots:INT = 15
r.instanceCount = nrDots
let angle= CGFloat(2 * M_PI)/ CGFloat(nrDots)
r.instanceTransform = CATransform3DMakeRotation(agnle : 0.0,0.0,1.0)
设置instanceCount为15,设置了旋转变换使用2π/ 15的角度。再次运行应用程序,你将会看到这样一个漂亮的画面:
你可以通过改变nrDots 到10,25或者其他值来轻松的达到你想要的效果,复制器乖乖计算几何和渲染点的克隆:
现在,让我们做一段1.5秒的动画。在到原来的点上做缩放变化:
let duration :CFTimeInterval = 1.5
let shrink = CABasicAnimation(keyPath:"transform.scale")
shrink.fromValue = 1.0
shrink.toValue = 0.1
shrink.duration = duration
shrink.repeatCount = Float.infinity
dot.addAnimation(shrink,forKey:nil)
这样就做出了一个催眠动画,所有的点一遍遍的变大变小。(千万不要盯着太久,以免被催眠哦)当你希望记住的方法,使动画动起来的秘诀就是给出一点延迟到每一个副本,就像这样
r.instanceDelay =duration/Double(nrDots)
这将让您的动画旋转很好。动画的第一个旋转有一个有点怪,但是在第一次旋转之后,所有的点就都是可见的为了解决这个问题,我们可以第一次的点进行缩放,在动画开始之前,加上这个代码最后一行到animation2() :
dot.transform = CATransform3DMakeScale(0.01,0.01,0.01)。
这将使动画变得更加流畅。
做到这,是否感觉做动画比想象中要简单的多,不是吗?如果你尝试了一下基本活动的指标上面的代码,你可以轻松地创建各种效果,给一个试试吧!
3.跟随动画
在本教程中的第三个是有趣的跟随动画,您将指定的动画原始图层上的路径,并让其拷贝在追逐,并视图追第一个点。切换到viewDidLoad中()和
把animation2()替换为:
func animation3()
正如你可能已经猜到你的下一个步骤就是添加一个空的方法你最终的动画:
func animation3(){
对于这个动画,您将需要添加另外一个方法。使用PaintCode应用程序,可以快速创建了一个贝塞尔路径,你会用你的动画。加入这个方法的ViewController:
func rw() -& CGPath {
////贝齐尔制图
变种bezierPath = UIBezierPath()
bezierPath.moveToPoint(CGPointMake(31.5,71.5))
bezierPath.addLineToPoint(CGPointMake(31.5,23.5))
bezierPath.addCurveToPoint(CGPointMake(58.5, 38.5),
controlPoint1:CGPointMake(31.5,23.5),
controlPoint2:CGPointMake(62.46,18.69))
bezierPath.addCurveToPoint(CGPointMake(53.5,45.5),
controlPoint1:CGPointMake(57.5,43.5),
controlPoint2:CGPointMake(53.5,45.5))
bezierPath.addLineToPoint(CGPointMake(43.5,48.5))
bezierPath.addLineToPoint(CGPointMake(53.5,66.5))
bezierPath.addLineToPoint(CGPointMake(62.5,51.5))
bezierPath.addLineToPoint(CGPointMake(70.5,66.5))
bezierPath.addLineToPoint(CGPointMake( 86.5,23.5))
bezierPath.addLineToPoint(CGPointMake(86.5,78.5))
bezierPath.addLineToPoint(CGPointMake(31.5,78.5))
bezierPath.addLineToPoint(CGPointMake(31.5,71.5))
bezierPath.closePath()
VAR T = CGAffineTransformMakeScale(3.0, 3.0)
return CGPathCreateCopyByTransformingPath(bezierPath.CGPath,&T)
此方法创建的代码贝塞尔路径,并返回它的CGPath副本 - 这CGPath你会用它来创建一个关键帧动画。切换到到animation3() ,在里面添加代码:
let r = CAReplicatorLayer()
r.bounds = view.bounds
r.backgroundColor = UIColor(white:0.0,alapha:0.75).CGColor
r.position = view.center
view.layer.addSublayer(R)
这个时候我们创建并添加一个和视图一样大小的空复制层。首先我们需要添加原始层复制层添加以下代码:
let dot = CALayer()
dot.bounds = CGRect(0.0, 0.0, 10.0, 10.0)
dot.backgroundColor = UIColor(white: 0.8,alpha1.0).CGColor
dot.borderColor = UIColor(white:1.0,alpha:1.0).CGColor
dot.borderWidth = 1.0
dot.cornerRadius = 5.0
dot.shouldRasterize = true
dot.rasterizationScale = UIScreen.mainScreen().scale
r.addSublayer(dot)
我们创建一个小银矩形,并设置矩形宽度的一半为圆角半径,所以你得到了一个小圈。我们对它进行复制。运行程序,我们将看到圆圈显示在屏幕的左上角。
让我们的动画沿着路径动起来:
let move = CAKeyframeAnimation(keyPath:"positon")
move.path = rw()
move.repeatCount = Float.infinity
move.duration = 4.0
dot.addAnimation(move,forKey:nil)
这个动画沿着由rw()方法绘制的途径,以4秒为周期,不停的运动着。
运行程序,现在我们会看到点像疯了一样乱跑,看不到它所走过的路径。为了让动画更加清晰,添加以下代码:
r.instanceCount = 20
r.instanceDelay = 0.1
这将增加的19个圆点,并让他们跟着第一个运行:
酷!动画开始运动就像你最喜欢的Logo一样。
我们来模仿的更加像一点,首先让颜色和网站的颜色的圆圈一样。添加该代码添加着色到复制的内容:
r.instanceColor = UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0).CGColor
设置instanceColor乘以您所提供的颜色原始内容的颜色。在这种情况下,您将乘颜色鲜艳的绿色,所以如果你运行的应用程序,你会看到一张很绿动画:)))有趣的是,你还可以改变instanceColor.它非常容易使得绿色的色调变得越来越黑加入这个代码最后一行:
r.instanceGreenOffset = -0.03
这将使复制每次减少了0.03的绿色色调成分。最终的动画看起来是这样的:
会不会有人立刻打开一个新的Xcode项目,开始在一个贪吃蛇游戏呢?:]
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
专题内容主要包括OC、swift等涉及到iOS开发进阶的内容。
swift可以关注下我的另一个专题:
swift开发...
· 19772人关注
Write the Code, Change the World, 改变世界从编程开始, 收集编程相关的干货
· 11639人关注
· 6499人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:iOS进阶(17)
项目地址:
对于模态展示(Modal)
iOS 8之后,可以通过设置ViewController的转场代理
transitioningDelegate
这个转场代理是一个协议类型UIViewControllerTransitioningDelegate.由于我们是非交互式转场,所以只需要实现协议的两个方法即可
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -& UIViewControllerAnimatedTransitioning? {
func animationControllerForDismissedController(dismissed: UIViewController) -& UIViewControllerAnimatedTransitioning? {
navigationController.delegate
这里的delegate是UINavigationControllerDelegate类型,在不考虑交互式转场的情况下,我们只需要实现以下方法即可
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -& UIViewControllerAnimatedTransitioning? {
switch operation {
case .Pop:
case .Push:
return nil
细心的朋友应该发现了,不管是模态还是Push,都是返回一个UIViewControllerAnimatedTransitioning协议类型的对象
通用的Animator
定义一个类,让其遵循UIViewControllerAnimatedTransitioning协议,来实现实际的动画,
class ImageMaskAnimator: NSObject,UIViewControllerAnimatedTransitioning {}
由于实现了UIViewControllerAnimatedTransitioning协议,所以要提供两个方法
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -& NSTimeInterval {
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
转场的原理
在上文提到的transitionDuration方法中,可以看到有一个参数是transitionContext,这个是转场上下文。通过转场上下文,可以获得fromView,toView以及containView,
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
let containView = transitionContext.containerView()!
其中,关系如图
转场开始的时候,上下文自动把FromView添加到转场ContainView
转场结束的时候,上下文自动把FromView移除ContainView
所以,开发者要做的就是
把toView添加到转场ContainView中,并且定义好toView的初始位和状态
定义好FromView和ToView的转场结束时候的状态
创建fromView到toView动画
用CIFilter截图并添加高斯模糊
细心的朋友能看到,在最上面转场的时候,不管是present还是dismiss,第一个Controller的看起来都是”模糊”的。其实是采用截图,然后添加一个ImageView覆盖到转场的ContainView上实现的效果。其中,截图,并添加模糊的代码如下
extension UIView{
func blurScreenShot(blurRadius:CGFloat)-&UIImage?{
guard self.superview != nil else{
return nil
UIGraphicsBeginImageContextWithOptions(CGSize(width: frame.width, height: frame.height), false, 1)
layer.renderInContext(UIGraphicsGetCurrentContext()!)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
guard let blur = CIFilter(name: "CIGaussianBlur") else{
return nil
blur.setValue(CIImage(image: image), forKey: kCIInputImageKey)
blur.setValue(blurRadius, forKey: kCIInputRadiusKey)
let ciContext
= CIContext(options: nil)
let result = blur.valueForKey(kCIOutputImageKey) as! CIImage!
let boundingRect = CGRect(x:0,
width: frame.width,
height: frame.height)
let cgImage = ciContext.createCGImage(result, fromRect: boundingRect)
let filteredImage = UIImage(CGImage: cgImage)
return filteredImage
Tips:在模拟器上,CIFilter的效率很差,但是真机上很快
Present/Push动画过程
首先,我们要做一些准备工作,其中,核心是坐标系转换
Swift通过以下代码块来实现条件编译,可以通过条件编译来适配多版本Swift
整个动画的准备过程
//获取必要参数
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
let containView = transitionContext.containerView()!
let frame = UIScreen.mainScreen().bounds
maskContentView = UIImageView(frame: frame)
maskContentView.backgroundColor = UIColor.lightGrayColor()
//如果是Present或者push
if self.transitionType == .Present {
//模拟器上禁用Blur
#if (arch(i386) || arch(x86_64)) && os(iOS)
print("Wow,CIFilter is too slow on simulator,So I disable blur on Simulator")
//截图,生成blur
self.blurImage = fromView.blurScreenShot(3.0)
maskContentView.image = fromView.blurScreenShot(3.0)
//Blur作为背景,添加到ContainView
maskContentView.frame = containView.bounds
containView.addSubview(self.maskContentView)
let fromImageView = self.config.fromImageView
//Frame的坐标系适配
let adjustFromRect = fromImageView.convertRect(fromImageView.bounds, toView: containView)
let toImageView = self.config.toImageView!
let adjustToRect = toImageView.convertRect(toImageView.bounds, toView: containView)
//添加一个ImageView,来显示动画
imageView = UIImageView(frame: adjustFromRect)
imageView.image = fromImageView.image
containView.addSubview(imageView)
//设置阴影
imageView.layer.shadowColor = UIColor.blackColor().CGColor
imageView.layer.shadowOffset = CGSizeMake(2.0, 2.0)
imageView.layer.shadowRadius = 10.0
imageView.layer.shadowOpacity = 0.8
//开始动画
//动画代码....
到这里,视图的层次结构如下
ContainView
maskContentView
整个Present的动画分为三个部分
//第一步,移动ImageView到toView的对应Frame,同时变化Scale
UIView.animateWithDuration(0.5 / 1.6 * self.config.presentDuration, animations: {
self.imageView.frame = adjustToRect
self.imageView.transform = CGAffineTransformMakeScale(1.2, 1.2)
}) { (finished) in
//第二步,恢复ImageView的Transfrom
UIView.animateWithDuration(0.3 / 1.6 * self.config.presentDuration, animations: {
self.imageView.transform = CGAffineTransformIdentity
self.imageView.layer.shadowOpacity = 0.0
}) { (finished) in
//第三步,添加toView,然后开始mask动画
containView.addSubview(toView)
containView.bringSubviewToFront(self.imageView)
let adjustFrame = self.imageView.convertRect(self.imageView.bounds, toView: self.maskContentView)
toView.maskFrom(adjustFrame, duration: 0.8 / 1.6 * self.config.presentDuration,complete: {
//所有动画完成,进行清理
self.maskContentView.removeFromSuperview()
self.imageView.removeFromSuperview()
self.maskContentView = nil
self.imageView = nil
//通知上下文,转场完成
transitionContext.completeTransition(true)
在最后一步进行Mask的时候,视图的层次如下
ContainView
maskContentView
Mask动画其实比较简单,我们都知道CALayer有一个mask属性,它也是一个CALayer,整个Mask动画的过程如下
用CAShapeLayer作为对应View的mask
通过动画CAShapeLayer的path属性-实现一个逐渐扩大的圆来实现对应的mask动画
maskLayer.path = toPath.CGPath
let basicAnimation = CABasicAnimation(keyPath: "path")
basicAnimation.duration = duration
basicAnimation.fromValue = fromPath.CGPath
basicAnimation.toValue = toPath.CGPath
basicAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
maskLayer.addAnimation(basicAnimation, forKey: "pathMask")
self.layer.mask = maskLayer
其中,fromPath是开始的圆环,toPath是结束的圆环。
圆环的绘制采用贝赛尔曲线
let fromPath = UIBezierPath(arcCenter: fromCenter, radius: fromRadius, startAngle: 0, endAngle: CGFloat(M_PI) * 2, clockwise: true)
这里用事务进行包裹,进而在动画完成的时候进行反馈
CATransaction.begin()
CATransaction.setCompletionBlock {
Dismiss/Pop的原理类似,不再赘述
完整代码地址:
欢迎Follow我的Github,LeoMobileDeveloper
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:421606次
积分:7727
积分:7727
排名:第1827名
原创:261篇
评论:106条
文章:54篇
阅读:96171
阅读:2173
阅读:19674
文章:14篇
阅读:36991
文章:14篇
阅读:32058
文章:18篇
阅读:29339
(1)(4)(5)(6)(4)(5)(1)(2)(4)(8)(13)(11)(7)(6)(9)(10)(21)(15)(17)(10)(15)(15)(25)(27)(17)(3)如何用 Swift 做一个复杂的加载动画_iOS大全-爱微帮
&& &&& 如何用 Swift 做一个复杂的加载动画
(点击上方公号,可快速关注)原文:raywenderlich译文:CocoaChina网址:/swift/27.html时至今日,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 类的顶端附近声明如下的常量let ovalLayer = OvalLayer()现在在此类的底部添加如下方法func addOval() { layer.addSublayer(ovalLayer) ovalLayer.expand()}这段代码首先添加了你上面创建的 OverLayer 的实例作为一个子层到视图层,然后调用 expand() 方法,这是被切掉的需要你来填充的函数之一来到 OvalLayer.swift 文件,添加如下代码到 expand() 中: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() 方法中添加如下的线条holderView.addOval()将 holdview 添加到 ViewController 的视图中后,调用 addOval 方法来启动动画构建并运行你的应用,你的动画现在就会看起来像下面(图例)摇动椭圆使用视图中扩张的椭圆,下一步就是在椭圆的步调中设置一些反弹,使之摇摆起来打开 HolderView.swift,在此类的底部,添加下面的函数func wobbleOval() { ovalLayer.wobble()}在 OvalLayer 中调用被切掉的方法 wobble().现在打开 OverLayer.swift,在 wobble() 中添加如下代码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() 结尾处添加如下代码NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: &wobbleOval&, userInfo: nil, repeats: false)在这里,你创建了一个timer定时器,它会在OvalLayer已经结束扩张后调用 wobbleOval()编译并运行你的应用,检查下你的新动画。这有点微妙,但那对一个真正的明快的动画是一个重要的因素。你不再需要那些满屏幕都是乱飞的东西了。开始变身是时候来电有趣的东西了。你将要把一个椭圆变身成为一个三角形。在用户眼里,这个转变应该看上去无缝连接的。要做到这些,你会用到两个相同颜色的分离的形状。打开HolderView.swift,在HolderView类的顶端稍微靠近你早些时候添加的 OvalLayer 属性的下面添加如下代码let triangleLayer = TriangleLayer()这里声明了一个 TriangleLayer 类的常量,正如你在 OvalLayer 中做的一样现在,让wobbleOval()方法看上去像这样: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()注意:找到动画的正确的间隔或延迟需要反复实验,这也是一个好的动画和一个极好的动画区别。我鼓励你去修补你的动画,让它们看上去完美。这可能要花点时间,但确是值得的。接下来,在此类的底部添加如下的函数。func drawAnimatedTriangle() { triangleLayer.animate()}这个方法会被你刚刚加入到 wobbleOval() 中的timer定时器调用。现在打开 TriangleLayer.swift,添加如下代码到 animate()func animate() { var triangleAnimationLeft: CABasicAnimation = CABasicAnimation(keyPath: &path&) triangleAnimationLeft.fromValue = trianglePathSmall.CGPath triangleAnimationLeft.toValue = trianglePathLeftExtension.CGPath triangleAnimationLeft.beginTime = 0.0 triangleAnimationLeft.duration = 0.3 var triangleAnimationRight: CABasicAnimation = CABasicAnimation(keyPath: &path&) triangleAnimationRight.fromValue = trianglePathLeftExtension.CGPath triangleAnimationRight.toValue = trianglePathRightExtension.CGPath triangleAnimationRight.beginTime = triangleAnimationLeft.beginTime + triangleAnimationLeft.duration triangleAnimationRight.duration = 0.25 var triangleAnimationTop: CABasicAnimation = CABasicAnimation(keyPath: &path&) triangleAnimationTop.fromValue = trianglePathRightExtension.CGPath triangleAnimationTop.toValue = trianglePathTopExtension.CGPath triangleAnimationTop.beginTime = triangleAnimationRight.beginTime + triangleAnimationRight.duration triangleAnimationTop.duration = 0.20 var 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 三角层单独隔离出来。打开 HolderView.swift,在 drawAnimatedTriangle(): 尾部添加如下代码NSTimer.scheduledTimerWithTimeInterval(0.9, target: self, selector: &spinAndTransform&, userInfo: nil, repeats: false)这里设置了一个定时器timer,用于在三角形动画结束后触发。0.9s的时间还次用反复实验来确定现在在这个类的底部添加如下的函数。func spinAndTransform() { // 1 layer.anchorPoint = CGPointMake(0.5, 0.6) // 2 var 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() 方法func contract() { var contractAnimation: CABasicAnimation = CABasicAnimation(keyPath: &path&) contractAnimation.fromValue = ovalPathLarge.CGPath contractAnimation.toValue = ovalPathSmall.CGPath contractAnimation.duration = animationDuration contractAnimation.fillMode = kCAFillModeForwards contractAnimation.removedOnCompletion = false addAnimation(contractAnimation, forKey: nil)}这段代码应用 CABasicAnimation 类,将 OvalLayer 设置它的初始路径 ovalPathSmall。构建并运行你的应用程序,当动画完成的时候,只有三角形应该被留在屏幕上。绘制容器在下面这部分,你将要绘画一个矩形容器,用于创建一个闭合圈。你将会用到 RectangleLayer 的描边属性。你需要这样做两次,将红色和蓝色都作为描边色。打开 HolderView.swift, 像下面这样声明两个 RectangularLayer 常量,(位置)就在你稍早时候 triangleLayer 属性的下面let redRectangleLayer = RectangleLayer()let blueRectangleLayer = RectangleLayer()接下来添加如下代码到 spinAndTransform(): 的尾部。NSTimer.scheduledTimerWithTimeInterval(0.45, target: self,
selector: &drawRedAnimatedRectangle&,
userInfo: nil, repeats: false)NSTimer.scheduledTimerWithTimeInterval(0.65, target: self,
selector: &drawBlueAnimatedRectangle&,
userInfo: nil, repeats: false)这里创建两个定时器timer分别调用 drawRedAnimatedRectangle() 和 drawBlueAnimatedRectangle() 。旋转动画结束后,首先需要画出矩形,当红色矩形描边绘画接近完成的时候,蓝色矩形描边开始。添加下面两个方法头此类的底部func drawRedAnimatedRectangle() { layer.addSublayer(redRectangleLayer) redRectangleLayer.animateStrokeWithColor(Colors.red)}func drawBlueAnimatedRectangle() { layer.addSublayer(blueRectangleLayer) blueRectangleLayer.animateStrokeWithColor(Colors.blue)}一旦你添加矩形层 RectangleLayer 作为 HolderView 的子层,你就要调用 animateStrokeWithColor(color:) 并通过适当的颜色来绘画出边线。现在打开 RectangleLayer.swift, 像下面这样填充 animateStrokeWithColor(color:)func animateStrokeWithColor(color: UIColor) { strokeColor = color.CGColor var 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,将会产生整个路径被抹去的错觉。编译并运行你的应用,查看两个描边是如何看起来像他们构建的容器的。填充容器动画的下一步就是填充容器。你要寻找到的效果就像是水填充到玻璃杯中。这是个非常棒的视觉特效,使之为一个大的飞溅特效打开 HolderView.swift,在 RectangleLayer 属性稍靠下添加如下的常量let arcLayer = ArcLayer()现在在drawBlueAnimatedRectangle():尾部添加如下的代码NSTimer.scheduledTimerWithTimeInterval(0.40, target: self, selector: &drawArc&,
userInfo: nil, repeats: false)这(段代码)创建了一个定时器,用于当蓝色 RectangleLayer 完成绘画后调用 drawArc()在类的结尾添加如下的函数func drawArc() { layer.addSublayer(arcLayer) arcLayer.animate()}这段代码是在你动画填充之前,添加了上面已经创建ArcLayer 的实例对象到HolderView 层。打开ArcLayer.swift 然后添加如下代码到animate():func animate() { var arcAnimationPre: CABasicAnimation = CABasicAnimation(keyPath: &path&) arcAnimationPre.fromValue = arcPathPre.CGPath arcAnimationPre.toValue = arcPathStarting.CGPath arcAnimationPre.beginTime = 0.0 arcAnimationPre.duration = animationDuration var arcAnimationLow: CABasicAnimation = CABasicAnimation(keyPath: &path&) arcAnimationLow.fromValue = arcPathStarting.CGPath arcAnimationLow.toValue = arcPathLow.CGPath arcAnimationLow.beginTime = arcAnimationPre.beginTime + arcAnimationPre.duration arcAnimationLow.duration = animationDuration var arcAnimationMid: CABasicAnimation = CABasicAnimation(keyPath: &path&) arcAnimationMid.fromValue = arcPathLow.CGPath arcAnimationMid.toValue = arcPathMid.CGPath arcAnimationMid.beginTime = arcAnimationLow.beginTime + arcAnimationLow.duration arcAnimationMid.duration = animationDuration var arcAnimationHigh: CABasicAnimation = CABasicAnimation(keyPath: &path&) arcAnimationHigh.fromValue = arcPathMid.CGPath arcAnimationHigh.toValue = arcPathHigh.CGPath arcAnimationHigh.beginTime = arcAnimationMid.beginTime + arcAnimationMid.duration arcAnimationHigh.duration = animationDuration var arcAnimationComplete: CABasicAnimation = CABasicAnimation(keyPath: &path&) arcAnimationComplete.fromValue = arcPathHigh.CGPath arcAnimationComplete.toValue = arcPathComplete.CGPath arcAnimationComplete.beginTime = arcAnimationHigh.beginTime + arcAnimationHigh.duration arcAnimationComplete.duration = animationDuration var 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, target: self, selector: &expandView&,
userInfo: nil, repeats: false)这(段代码)创建了一个定时器,用于在 ArcLayer 填充到容器后调用 expandView()现在,添加下面的函数到同一个类的底部:func expandView() { // 1 backgroundColor = Colors.blue // 2 frame = CGRectMake(frame.origin.x - blueRectangleLayer.lineWidth,
frame.origin.y - blueRectangleLayer.lineWidth,
frame.size.width + blueRectangleLayer.lineWidth * 2,
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().在类的底部,添加如下函数func addLabel() { delegate?.animateLabel()}这里只是简单的调用视图的代理函数,展示label标签。现在打开ViewController.swift,添加如下代码到animateLabel():func animateLabel() { // 1 holderView.removeFromSuperview() view.backgroundColor = Colors.blue // 2
var label: UILabel = UILabel(frame: view.frame) label.textColor = Colors.white label.font = UIFont(name: &HelveticaNeue-Thin&, size: 170.0) label.textAlignment = NSTextAlignment.Center label.text = &S& label.transform = CGAffineTransformScale(label.transform, 0.25, 0.25) view.addSubview(label) // 3
UIView.animateWithDuration(0.4, delay: 0.0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: UIViewAnimationOptions.CurveEaseInOut, animations: ({ label.transform = CGAffineTransformScale(label.transform, 4.0, 4.0) }), completion: { finished in self.addButton() })}依次带入各个注释段从视图中移除HolderView ,并设置视图的背景颜色为蓝色。创建一个文本为”S”的UIlabel标签对象,用于展示logo,并添加到视图。标签对象使用一个弹性动画来使之伸缩。一旦动画结束,调用 addButton() 来添加一个按钮到视图中,当按钮按下的时候,重复动画。构建并运行应用程序,花个时间来欣赏自己构建的动画吧。下一步你可以从这里
最终完整的项目。这个指导书包含了相当多的不一样的动画技术,当这些动画都堆叠在一起的时候,能够创造一个相当复杂的加载动画,这确实能够让你的应用在第一次被(用户)运行的时候就眼前一亮。从这里,放松自由的玩玩不一样的(动画 的)定时和形状,看看你能组装成哪些很酷的动画iOS大全微信号:iOShub打造东半球最好的 iOS 微信号--------------------------------------商务合作QQ:投稿网址:
点击展开全文
悄悄告诉你
更多同类文章
还可知道有多少人阅读过此篇文章哦
阅读原文和更多同类文章
可微信扫描右侧二维码关注后
还可知道有多少人阅读过此篇文章哦
伯乐在线旗下账号,分享 iOS 和 Mac 相关技术文章、工具资源、精选课程、热点资讯
您的【关注和订阅】是作者不断前行的动力
本站文章来自网友的提交收录,如需删除可进入
删除,或发送邮件到 bang@ 联系我们,
(C)2014&&版权所有&&&|&&&
京ICP备号-2&&&&京公网安备34

我要回帖

更多关于 swift 动画效果 的文章

 

随机推荐