>

UIView Animation(一)-常用API使用(Swift)

- 编辑:澳门新葡亰平台游戏 -

UIView Animation(一)-常用API使用(Swift)

Animate changes to one or more views using the specified duration, delay, options, and completion handler.对一个或者多个视图按照相应参数做固定动画(翻译纯属个人见解,有错请指出)

放在 performWithoutAnimation 闭包中就会不执行动画了, 这个在有时候做项目的时候某个功能总是会莫名其妙的调一下或者执行一个很奇怪的动画,这时候可以把那段 code 放在这个闭包中,就不会有动画了。

本文及其之后的讲解是依据Raywenderich 的 Animation Tutorial这一本书和苹果官方文档总结而来,特此声明!

自定义 animation property

我们可以在自己自定义的view上自定义一个可以动画的属性。例如加一个swing 属性,设置为true 则 center.x 加 100 , false 则 center.x 减 100

class MyView: UIView { var swing: Bool = true { didSet{ var p = self.center p.x = self.swing ? p.x + 100 : p.x - 100 UIView.animate(withDuration: 0) { self.center = p } } }}

然后在动画的时候只需要使用swing属性就可以了

UIView.animate(withDuration: 0.4) { self.view1.swing = !self.view1.swing}

图片 1示例

为当前视图建立可以容纳一个或多个关键帧动画对象的动画块,然后根据指定的时间一帧帧的执行指定动画,需要与addKeyframeWithRelativeStartTime: relativeDuration: animations:结合使用,注意:如果在block中没用添加关键帧动画对象,动画还是会执行,只不过跟调用animateWithDuration(duration: delay: options: animations: completion:效果一样!简单点来说,调用该API就是创建了一个动画容器,然后可以向这个容器中添加多个动画!示意图:**

取消view的动画

一旦一个动画开始执行,在执行的过程中我们怎么取消呢?下面有一个执行时间很长的动画。

self.pOrig = self.view4.centerself.pFinal = self.view4.centerself.pFinal.x -= 100UIView.animate(withDuration: 4) { self.view4.center = self.pFinal}

这个动画执行很漫长,我中途想取消怎么办 ?

  • 调用 layer 层的 removeAllAnimations
self.view4.layer.removeAllAnimations()

图片 2示例

[图片上传中...(pic_02.gif-92d0a1-1511337092782-0)]

这种方式会跳动一下,很不好。

  • 我们可以记录用一个0.1秒的动画到终点,先拿到当前的位置。
self.view4.layer.position = self.view4.layer.presentation()!.positionself.view4.layer.removeAllAnimations()UIView.animate(withDuration: 0.1) { self.view4.center = self.pFinal}

图片 3示例

这样看起来 smooth 多了。

view的 transform 非常简单,也比较常用,就旋转平移缩放,可以叠加在一起使用。

UIView.animate(withDuration: 1.2) { self.view5.transform = CGAffineTransform.identity .translatedBy(x: -100, y: 0) .rotated(by:CGFloat(Double.pi/4)) .scaledBy(x: 0.5, y: 0.5)}

图片 4示例

如果需要回到原来的位置 用 self.view5.transform = CGAffineTransform.identity 即可

  • addKeyframeWithRelativeStartTime:relativeDuration:animations:

所有示例代码均可以在 Animations-Demo 下载到

由于帧动画开始时间和持续时间均为相对时间,取值范围均为,可以用一个简单的公式来计算实际时间:实际时间 = 整个动画时长 * 参数值

Performs a view animation using a timing curve corresponding to the motion of a physical spring.对一个视图按照相应参数做弹性动画(类似于弹簧,翻译纯属个人见解,有错请指出)

图片借鉴Renfei Song's Blog,只为更清楚的展示调用两个API的不同效果Spring Animation 和普通的动画的运动曲线的对比:

图片 5Spring Animation, Ease-Out Animation 和 Linear Animation 的动画效果图片 6

Keyframe 关键帧动画

什么意思呢?就是我们可以把动画分成一个一个小的阶段,然后在将这些结合在一起。使用 UIView.animateKeyframes(withDuration:,delay:,options:,animations:,completion:) 然后在 animations 的闭包中调用 UIView.addKeyframe(withRelativeStartTime: , relativeDuration: , animations:) 添加关键帧 。可以多次调用指定多个点。 startTime 是从0 - 1 的相对时间 ,相对于整体动画的时间。看个例子 。

var p = self.view3.centerlet dur = 0.25var start = 0.0let dx: CGFloat = -100let dy: CGFloat = 50var dir: CGFloat = 1UIView.animateKeyframes(withDuration: 4, delay: 0, options: [], animations: { UIView.addKeyframe(withRelativeStartTime: start , relativeDuration: dur , animations: { p.x += dx*dir p.y += dy self.view3.center = p }) start += dur dir *= -1 UIView.addKeyframe(withRelativeStartTime: start , relativeDuration: dur , animations: { p.x += dx*dir p.y += dy self.view3.center = p }) start += dur dir *= -1 UIView.addKeyframe(withRelativeStartTime: start , relativeDuration: dur , animations: { p.x += dx*dir p.y += dy self.view3.center = p }) start += dur dir *= -1 UIView.addKeyframe(withRelativeStartTime: start , relativeDuration: dur , animations: { p.x += dx*dir p.y += dy self.view3.center = p })}, completion: nil)

图片 7示例

上面的示例,一共有四个关键帧。每个relativeDuration 0.25, 表示占总时长的1/4。四段等时长的帧动画。我们上面并没有指定options,这里使用的是UIViewKeyframeAnimationOptions,默认是 .calculationModeLinear . 当然我们也可以指定其他的option,这个作用可以自己去尝试。我们的关键帧动画的必报里也是可以指定多个属性、或者多个view的动画。

class func animateKeyframesWithDuration(_ duration: NSTimeInterval, delay delay: NSTimeInterval, options options: UIViewKeyframeAnimationOptions, animations animations: () -> Void, completion completion:  -> Void)?)
  • 转场动画

UIView 动画 options

UIView 动画比较完整的版本并不是上面那么简短,还有很多其他的参数可以配置

animate(withDuration:, delay: , options: , animations: , completion: )
  • withDuration:动画的持续时间,也可理解为动画的执行速度,持续时间越小速度越快
  • delay:动画开始之前的延时,默认是无延时。
  • options:一个附加选项,UIViewAnimationOptions 可以指定多个
  • animations:执行动画的闭包
  • completion:动画完成后执行的闭包,可以为nil,可以在这里链接下一个动画。

下面是一些主要的options (UIViewAnimationOptions) :

  • 动画执行对应的曲线: 动画执行过程速度的改变,会有一个加速度或者一个减速度。
    • .curveEaseIn
    • .curveEaseOut
    • .curveEaseInOut
    • .curveLinear
  • .repeat : 指定这个选项后,动画会无限重复
  • .autoreverse:往返动画,从开始执行到结束后,又从结束返回开始。

但是这里会有个问题,如果我们让一个view 移动100pt。使用 .autoreverse , 代码如下

let opts = UIViewAnimationOptions.autoreverseUIView.animate(withDuration: 1, delay: 0, options: opts, animations: { self.view2.center.x -= 100}, completion: nil)

图片 8示例

发现很顺利的往返之后,又跳了一下,是因为我们view2的center 其实已经改变了。如果想让它回到原位,只需要在完成时候指定它的位置即可

let xorig = self.view2.center.xlet opts = UIViewAnimationOptions.autoreverseUIView.animate(withDuration: 1, delay: 0, options: opts, animations: { self.view2.center.x -= 100}, completion: { _ in self.view2.center.x = xorig})

图片 9示例

如果想让这个动画无限次循环,只需要加一个option

let opts: UIViewAnimationOptions = [.autoreverse , .repeat]

如果需要执定循环次数

let xorig = self.view2.center.xlet opts: UIViewAnimationOptions = UIViewAnimationOptions.autoreverseUIView.animate(withDuration: 1, delay: 0, options: opts, animations: { UIView.setAnimationRepeatCount self.view2.center.x -= 100}, completion: { _ in self.view2.center.x = xorig})

这样就会只循环五次

还有一些options指定如果另一个动画已经作用在这个view上时,该怎么办。

  • .beginFromCurrentState 从上次动画的当前状态继续这次的动画,立即执行上次动画的完成b闭包。会使用 presentation layer 决定从哪里开始。如果可能的化,会混合两次的动画。
  • .overrideInheritedDuration 不继承别的动画的持续时间
  • .overrideInheritedCurve 不继承别的动画的曲线

iOS 8之后.beginFromCurrentState 就很少用到了,在 iOS 7 时候下面的动画是会跳一下,现在都很平滑了

UIView.animate(withDuration: 0.5) { self.view3.center.x -= 100}UIView.animate(withDuration: 0.5) { self.view3.center.y += 100}

图片 10示例

图片 11普通动画API调用一次只能做同一类型动画(从动画开始到动画结束);而关键帧动画API调用一次,在同一时间段内可以做多种类型动画,各帧动画开始时间和结束时间都可自定义还需注意一点,与其他动画API的options不同的是,关键帧动画独有的UIViewKeyframeAnimationOptions

class func animateWithDuration(_ duration: NSTimeInterval, delay delay: NSTimeInterval, usingSpringWithDamping dampingRatio: CGFloat, initialSpringVelocity velocity: CGFloat, options options: UIViewAnimationOptions, animations animations: () -> Void, completion completion:  -> Void)?)

Transitions 过渡

过度动画强调的是view改变内容。一般有两个方法

  • UIView.transition(with:, duration:, options:, animations:, completion:)
  • UIView.transition(from: , to:, duration:, options:, completion:)

过渡动画的类型是一个options (UIViewAnimationOptions

  • .transitionFlipFromLeft,.transitionFlipFromRight
  • .transitionFlipFromTop,.transitionFlipFromBottom
  • .transitionCurlUp,.transitionCurlDown
  • .transitionCrossDissolve

来看一个例子,我们来修改下 UIImageView 的内容

UIView.transition(with: self.imageView , duration: 0.6 , options: .transitionFlipFromLeft , animations: { if self.imageView.image == #imageLiteral(resourceName: "rabbit") { self.imageView.image = #imageLiteral(resourceName: "elephant") }else{ self.imageView.image = #imageLiteral(resourceName: "rabbit") }}, completion: nil)

从swift 3 开始 图片对象只要在资源里能找到都可以直接输出来,这里copy过来显示的是#imageLiteral(resourceName: "rabbit")实际是这样的

图片 12示例

运行效果:

图片 13示例

我们也可以对自定义view的draw rect 最transition。

class MyView1: UIView { var reverse = false override func draw(_ rect: CGRect) { let f = self.bounds.insetBy(dx: 10, dy: 10) let context = UIGraphicsGetCurrentContext() if self.reverse { context?.strokeEllipse }else{ context?.stroke } }}

动画部分只需要重新绘制即可。

self.view4.reverse = !self.view4.reverseUIView.transition(with: self.view4 , duration: 0.6 , options: .transitionFlipFromLeft , animations: { self.view4.setNeedsDisplay()}, completion: nil)

图片 14示例

默认情况下,一个视图的子视图在transition动画期间改变layout,这个改变是不会有动画的,将在transition结束的时候直接改变,如果想要做动画改变,需要加上.allowAnimatedContent option

UIView.transition(from: , to:, duration:, options:, completion:) 这个方法需要两个view ,第一个会被第二个替换掉。整个transition动画会在他们的superview进行动画。有两种可能的情况。

  • 删除一个subview , 添加另一个如果.showHideTransitionViews 不包含在 options 中 ,那么第二个view在我们开始动画时,并不在视图层级。transition动画 remove将第一个view从superview上remove掉,将第二个view添加到相同的superview上。

  • 隐藏一个subview , 显示另一个如果.showHideTransitionViews 包含在options 中 , 那么两个subview一开始都在视图层级中。第一个view的isHidden是false , 第二个为true 。 transition动画会对调两个view的isHidden属性。

let lab2 = UILabel(frame: self.label1.frame)lab2.text = self.label1.text == "Hello" ? "World" : "Hello"lab2.textColor = UIColor.whitelab2.sizeToFit()UIView.transition(from: self.label1 , to: lab2 , duration: 0.8 , options: .transitionFlipFromLeft , completion: { _ in self.label1 = lab2})

图片 15示例

class func transitionFromView(_ fromView: UIView, toView toView: UIView, duration duration: NSTimeInterval, options options: UIViewAnimationOptions, completion completion:  -> Void)?)
UIView.animate(withDuration: 0.4) { self.v.backgroundColor = UIColor.red self.v.center.y += 100 self.v2.alpha = 0}

图片 16

iOS 中实现动画有好几种方式,UIView 无疑是最简单的一种,但是所有的动画归根结底还是 layer 层的动画。UIView 层面的动画只是对 layer 层部分属性的封装。我们可以直接对 UIView 的 alphaboundscenterframetransformbackgroundColor(如果view没有实现draw)。上面这些属性看起来不多,但是足够满足大部分日常开发动画。

1 frameStartTime:帧动画开始时间,取值范围为,开始时间是相对于整个动画时间,整个关键帧动画时长6秒,设置开始时间为0.5,那么这一帧动画的实际开始时间为第3秒!2 frameDuration:帧动画持续时间,取值范围为,持续时间也是相对于整个动画时间,算法同上!

Creates a transition animation between the specified views using the given parameters.在两个给定视图之间构建过渡动画(翻译纯属个人见解,有错请指出)

图片 17示例

  • animateKeyframesWithDuration:delay:options:animations:completion:
  • animateWithDuration:delay:options:animations:completion:
let effect = UIBlurEffect(style: .dark)effectView.effect = nilUIView.animate(withDuration: 1) { self.effectView.effect = effect}

animateKeyframesWithDuration:delay:options:animations:completion:结合使用,用来指定帧动画开始时间,持续时间和执行操作,调用一次就可以添加一个帧动画!参数解读

Creates a transition animation for the specified container view.为指定的视图构建一个过渡动画(翻译纯属个人见解,有错请指出)

iOS 9 开始 , UIVisualEffectVieweffect 属性也是可以动画的。动画前设置为 nil ,在animations: 的block 中设置 effect

The view properties you change in the animations block animate over the timespan you specify in frameDuration parameter. The properties do not begin animating until the time you specify in the frameStartTime parameter. After the frame start time, the animation executes over its specified duration or until interrupted by another animation.

  • transitionWithView:duration:options:animations:completion:
UIView.animate(withDuration: 0.4) { self.v.backgroundColor = UIColor.red}

demo使用了关键帧动画API以及普通动画API

闭包中可以同时执行多个属性的动画,也可以是多个view的动画

class func addKeyframeWithRelativeStartTime(_ frameStartTime: Double, relativeDuration frameDuration: Double, animations animations: () -> Void)
  • 普通动画

Spring 弹性动画

弹性动画一般有一个很快的初速度,在结束的时候也会有一个摆动。类似弹簧的效果。

UIView.animate(withDuration: 1 , delay: 0 , usingSpringWithDamping: 0.3 , initialSpringVelocity: 8 , options: [] , animations: { self.view2.center.x -= 100}, completion: nil)

图片 18示例

usingSpringWithDamping 小于1 ,动画在最终位置都会有一个摇摆。值越小摇晃的越缓和。initialSpringVelocity 表示一个初始速度, 动画执行快慢由他和duration共同决定。这个需要根绝实际情况多多调试。

This method creates an animation block that you can use to set up a keyframe-based animation. The keyframes themselves are not part of the initial animation block you create using this method. Inside the animations block, you must add the keyframe time and animation data by calling the **addKeyframeWithRelativeStartTime: relativeDuration: animations: **method one or more times. Adding keyframes causes the animation to animate the view from its current value to the value of the first keyframe, then to the value of the next keyframe, and so on at the times you specify.

*如解释有误,请指出,谢谢* 1 duration: 动画执行时间2 delay:动画延迟执行时间3 options: 基本参数: .LayoutSubviews:在AutoLayout下,如果修改AutoLayout,那么子视图也会跟着一起变化 .AllowUserInteraction:在动画时,允许用户交互 .BeginFromCurrentState:允许在动画执行时执行新的动画 .Repeat:永远重复的运行 .Autoreverse:动画执行结束后按照相反的行为继续执行,该属性只能和.Repeat属性组合使用 .OverrideInheritedDuration:强制动画使用内层动画的时间值 .OverrideInheritedCurve:强制动画使用内层动画曲线值 线性参数: .CurveLinear:动画做线性运动 .CurveEaseIn:动画缓慢开始,然后逐渐加速 .CurveEaseOut:动画迅速开始,在结束时减速 .CurveEaseInOut:动画慢慢开始,然后加速,在结束之前减速 转场参数: .TransitionNone:没有转场动画 .TransitionFlipFromTop :从顶部围绕水平轴做翻转动画 .TransitionFlipFromBottom:从底部围绕水平轴做翻转动画 .TransitionFlipFromLeft :从左侧围绕垂直轴做翻转动画 .TransitionFlipFromRight:从右侧围绕垂直轴做翻转动画 .TransitionCurlUp:从下往上做翻页动画 .TransitionCurlDown :从上往下做翻页动画 .TransitionCrossDissolve:视图溶解消失显示新视图动画 4 usingSpringWithDamping:弹簧阻力,取值范围为0.0-1.0,数值越小“弹簧”振动效果越明显。 5 initialSpringVelocity:动画初始的速度,数值越大初始速度越快。但要注意的是,初始速度取值较高而时间较短时,也会出现反弹情况。

在 UIView 中执行一个动画非常简单,系统已经帮我们封装好了一切,你只需要将你想要动画的属性放到 animations 的闭包中即可。

1 .CalculationModeLinear:在帧动画之间采用线性过渡2 .CalculationModeDiscrete:在帧动画之间不过渡,直接执行各自动画3 .CalculationModePaced:将不同帧动画的效果尽量融合为一个比较流畅的动画4 .CalculationModeCubic:不同帧动画之间采用Catmull-Rom算法过渡5 .CalculationModeCubicPaced:3和4结合,试了就知道什么效果了

ImageView 和 Image 动画

UIImageView 上执行动画非常简单,只需要提供animationImages 属性。一个UIImage 数组。这个数组代码一个一个的帧,当我们调用 startAnimating 方法的时候,这个数组的图片就会轮流播放。animationDuration 决定了播放的速度。animationRepeatCount指定重复次数 (默认是0 , 代表无限重复),或者调用stopAnimating 方法停止动画。

例子 :

let rabbit = #imageLiteral(resourceName: "rabbit")UIGraphicsBeginImageContextWithOptions(rabbit.size , false, 0)let empty = UIGraphicsGetImageFromCurrentImageContext()!UIGraphicsEndImageContext()let arr = [rabbit,empty,rabbit,empty,rabbit]imageView.animationImages = arrimageView.animationDuration = 2imageView.animationRepeatCount = 3imageView.startAnimating()

图片 19示例

UIImage 有一些类方法为 UIImageView 构造 可以动画的image :

  • UIImage.animatedImage(with:, duration:)直接指定了image数组和duration。
  • UIImage.animatedImageNamed(, duration: )提供一个单个的image name , 系统会自动在后面加 "0" 。使这个image成为第一个image。最后一位数字累加。(知道没有图片或者到达”1024“)
  • UIImage.animatedResizableImageNamed(, capInsets: , duration: )跟上面的方式差不多,但是同时对每个image做了拉伸或者平铺。 图像本身也有resizableImage(withCapInsets: , resizingMode: )方法可以缩放(指定某个区域的拉伸或者平铺)
let im = UIImage.animatedImageNamed("voice", duration: 2)imageView2.image = im

其中voice1-3 已经命名好,放在Assets.xcassets

图片 20示例

我们再在一个 button上画一个圆从大到小的动画

var arr = [UIImage]()let w : CGFloat = 18for i in 0 ..< 6 { UIGraphicsBeginImageContextWithOptions(CGSize(width: w, height: w), false, 0) let context = UIGraphicsGetCurrentContext()! context.setFillColor(UIColor.red.cgColor) let ii = CGFloat let rect = CGRect(x: ii, y:ii, width: w-ii*2, height: w-ii*2) context.addEllipse context.fillPath() let im = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() arr.append}let im = UIImage.animatedImage(with: arr, duration: 0.5)self.button.setImage(im, for: .normal)

图片 21示例

本文及其之后的讲解是依据Raywenderich 的 Animation Tutorial这一本书和苹果官方文档总结而来,特此声明!

class func transitionWithView(_ view: UIView, duration duration: NSTimeInterval, options options: UIViewAnimationOptions, animations animations:  -> Void)?, completion completion:  -> Void)?)
UIView.animate(withDuration: 0.4) { self.v.backgroundColor = UIColor.red UIView.performWithoutAnimation { self.v2.alpha = 0 }}

To animate view properties during a keyframe animation, call this method from within the animation block you pass to the animateKeyframesWithDuration:delay:options:animations:completion: method. To animate between different values, or to tweak the timing of your view property animations, you can call this method multiple times within a block.

  • animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:

如果我们想让一个属性在 animations 闭包中执行,但是又不要执行动画,可以这样。

官方文档:

class func animateWithDuration(_ duration: NSTimeInterval, delay delay: NSTimeInterval, options options: UIViewAnimationOptions, animations animations: () -> Void, completion completion:  -> Void)?)
  • 效果图

    图片 22

  • Demo下载关键帧动画Demo

  • transitionFromView:toView:duration:options:completion:

If you do not add any keyframes in the animations block, the animation proceeds from start to end like a standard animation block. In other words, the system animates from the current view values to any new values over the specified duration.

示意图

  • 参数解释

官方文档:

本文由java编程发布,转载请注明来源:UIView Animation(一)-常用API使用(Swift)