IOS动画技术详解

视图动画

UIView动画块

ios4.0之后在UIView类中提供以下几个处理动画的方法。

+animateWithDuration:animations
+animateWithDuration:animations:completion
+animateWithDuration:delay:options:animations:completion

其中animateWithDuration后面的参数是动画的持续时间。而animations后面是一个block代码块,具体的动画内容就写在这个代码块里面。delay是动画延迟多少时间后执行,如果设置为0就表示马上执行。好啦,接下来我们来看一个例子吧。动画效果如下:


IOS动画技术详解_第1张图片
import UIKit

class AnimationBlokViewController: UIViewController {
    @IBOutlet weak var ball: UIImageView!//关联UIImageView为图片设置动画
    var flag = 1
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
//为button设置一个点击事件,点击后执行动画
    @IBAction func click(sender: AnyObject) {
        UIView.animateWithDuration(1.5) {
            var frame = self.ball.frame
            frame.origin.y += CGFloat(100 * self.flag)
            self.flag *= -1 
            self.ball.frame = frame
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
过渡动画

ios4.o之后在UIView中增加了两个过度动画的方法
+transitionWithView:duration:options:animations:completion:制定的容器内创建过渡动画。
+transitionFromView:toView:duration:options:completion:指定的两个视图之间创建过渡动画。
接下来让我们看看一个例子吧。在视图中创建4个button分别关联下面四个方法。点击就执行对应的过渡动画。值得注意的是其中options参数含有多个值时在oc中是按位或符号|将多个参数连接起来,而在swift中会报错,在swift中我们使用传递数组的形式[]传递多个参数。
相关的options参数有:
1.TransitionFlipLeft:从左往右翻转
2.TransitionFlipRight:从右往左翻转
3.TransitionCurlUp:向上翻页
4.TransitionCurlDown:向下翻页
5.TransitionCrossDissolve:交叉溶解效果
6.TransitionFlipFromTop:从上往下翻转
7.TransitionFlipFromBottom:从下往上翻转
8.CurveEaseInOut:缓入缓出,开始和结束时减速
9.CurveEaseIn:缓入,开始时减速
10.CurveEaseOut:缓出,结束时减速
11.CurveEaseLinear:线性,匀速运动
其中前七个或者后四个不能同时有两个或者以上在一个数组中使用否则会冲突。
动画效果如下:


import UIKit

class TrasitionViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
//从左往右翻转
    @IBAction func FlipFromLeft(sender: AnyObject) {
        UIView.transitionWithView(self.view, duration: 3.0, options:[UIViewAnimationOptions.CurveEaseOut,UIViewAnimationOptions.TransitionFlipFromLeft], animations: {
            print("动画开始")
            }) { (finished) in
                print("动画完成")
        }
    }

//从右往左翻转
    @IBAction func FlipFromRight(sender: AnyObject) {
        UIView.transitionWithView(self.view, duration: 3.0, options: [UIViewAnimationOptions.CurveEaseOut,UIViewAnimationOptions.TransitionFlipFromRight], animations: {
            print("动画开始")
        }) { (finished) in
            print("动画完成")
        }
    }
    
//向上翻页
    @IBAction func CurlUP(sender: AnyObject) {
        UIView.transitionWithView(self.view, duration: 3.0, options: [UIViewAnimationOptions.CurveEaseOut,UIViewAnimationOptions.TransitionCurlUp], animations: {
            print("动画开始")
        }) { (finished) in
            print("动画完成")
        }
    }
    
//向下翻页
    @IBAction func CurlDown(sender: AnyObject) {
        UIView.transitionWithView(self.view, duration: 3.0, options: [UIViewAnimationOptions.CurveEaseOut,UIViewAnimationOptions.TransitionCurlDown], animations: {
            print("动画开始")
        }) { (finished) in
            print("动画完成")
        }
    }
}```
####自定义过渡动画
视图之间过渡,跳转有两种情况,一种是树形结构导航,而另一种是模态视图导航。树形结构导航是通过UINavigationController控制视图的堆栈实现视图的过渡,模态导航是通过UIViewController控制视图实现的。针对这两种跳转,我们的自定义过渡动画也分为这两种。
在ios自定义过渡动画类需要实现UIViewControllerAnimateTransitiong协议的方法。其中有两个重要的方法。
1.-animateTransition:执行自定义的动画。
2.-transitionDuration:设置动画执行的时间。
接下来我们还是用实例说话:将模态导航和树形结构导航用法一起演示一遍:
![](http://chuantu.biz/t5/33/1473579787x1987391531.gif)

import UIKit

class SlideTransitionAnimator: NSObject,UIViewControllerAnimatedTransitioning {

func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
    return 1.5
}

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
    let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
    let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
    let transformedStartFrame = toVC!.view.frame
    
    let origin = transformedStartFrame.origin
    let width = transformedStartFrame.width
    let height = transformedStartFrame.height
    let transformedEndframe = CGRectMake((origin.x)-width, origin.y,width, height)
    transitionContext.containerView()?.addSubview((fromVC?.view)!)
    transitionContext.containerView()?.addSubview((toVC?.view)!)
    
    UIView.animateWithDuration(3, animations: {
        toVC?.view.frame = transformedEndframe
        toVC?.view.alpha = 0.5
        }) { (finished) in
            toVC?.view.frame = transformedEndframe
            toVC?.view.alpha = 1.0
            transitionContext.completeTransition(true)
    }
}

}

import UIKit

class CustomViewController: UIViewController ,UINavigationControllerDelegate,UIViewControllerTransitioningDelegate{

override func viewDidLoad() {
    super.viewDidLoad()
    self.navigationController?.delegate = self
    self.transitioningDelegate = self
}

@IBAction func treeClick(sender: AnyObject) {
    let VC = UIGravityViewController()
    self.navigationController?.showViewController(VC, sender: nil)
}

@IBAction func presentClick(sender: AnyObject) {
    let VC = UIGravityViewController()
    self .presentViewController(VC, animated: true) { 
        print("过渡完成")
    }
    
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   
        let destionViewController = segue.destinationViewController as UIViewController
        destionViewController.transitioningDelegate = self
}

func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    let animator = SlideTransitionAnimator()
    return animator
}

func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    let animator = SlideTransitionAnimator()
    return animator
}

 func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    let animator = SlideTransitionAnimator()
    return animator
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

}

####UIKit力学
UIKit力学是IOS7推出的技术,作用是使视图对象具有真实的物理运动效果。
其中包括的力学效果有:
#####1.重力行为(UIGravityBehavior)
重力行为能够模拟视图对象受重力影响而掉落的效果。效果图如下:

![gravity.gif](http://upload-images.jianshu.io/upload_images/1746439-3c60e669c4bd15fa.gif?imageMogr2/auto-orient/strip)

import UIKit

class UIGravityViewController: UIViewController {
@IBOutlet weak var ballImageView: UIImageView!
var animator : UIDynamicAnimator!
var gravity : UIGravityBehavior!

override func viewDidLoad() {
    super.viewDidLoad()
    self.title = "重力行为"
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.animator = UIDynamicAnimator(referenceView: self.view)
    //实例重力行为
    self.gravity = UIGravityBehavior(items: [self.ballImageView])
    //设置重力的方向
    let gravityDirection : CGVector = CGVectorMake(0.0, 0.1)
    self.gravity.gravityDirection = gravityDirection
    self.animator.addBehavior(self.gravity) 
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

}```

2.碰撞行为(UICollisionBehavior)

如同物理世界一样物体碰撞会反弹效果。效果图如下:

IOS动画技术详解_第2张图片
collision.gif
import UIKit

class UICollisonViewController: UIViewController {
    @IBOutlet weak var ballImageView: UIImageView!
    
    var collision : UICollisionBehavior!
    var animator : UIDynamicAnimator!
    var gravity : UIGravityBehavior!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "碰撞行为"
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.animator = UIDynamicAnimator(referenceView: self.view)
        //重力行为
        self.gravity = UIGravityBehavior(items: [self.ballImageView])
        let gravityDirection : CGVector = CGVectorMake(0.0, 0.1)
        self.gravity.gravityDirection = gravityDirection
        self.animator.addBehavior(self.gravity)
        //碰撞行为
        self.collision = UICollisionBehavior(items: [self.ballImageView])
        self.collision.translatesReferenceBoundsIntoBoundary = true
        self.animator.addBehavior(self.collision)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}```
#####3.吸附行为(UIAttachmentBehavior)
吸附行为可以实现两个物体彼此牵制,就好像用一根棍子将两个物体连接在一起。效果图如下:

![attachment.gif](http://upload-images.jianshu.io/upload_images/1746439-6d3bd327c889af58.gif?imageMogr2/auto-orient/strip)

import UIKit

class UIAttachmentViewController: UIViewController,UICollisionBehaviorDelegate {

@IBOutlet weak var attachmentPoint: UIImageView!
@IBOutlet weak var box: UIImageView!
@IBOutlet weak var barrier: UIImageView!
var collision : UICollisionBehavior!
var animator : UIDynamicAnimator!
var gravity : UIGravityBehavior!
var attach : UIAttachmentBehavior!

override func viewDidLoad() {
    super.viewDidLoad()
    self.title = "吸附行为"
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.animator = UIDynamicAnimator(referenceView: self.view)
    //重力行为
    self.gravity = UIGravityBehavior(items: [self.box])
    self.animator.addBehavior(self.gravity)
    //碰撞行为
    self.collision = UICollisionBehavior(items: [self.box])
    let width = self.barrier.frame.size.width
    let origin = self.barrier.frame.origin
    self.collision.addBoundaryWithIdentifier("barrier", fromPoint: origin, toPoint: CGPointMake(origin.x+width, origin.y))
    self.collision.translatesReferenceBoundsIntoBoundary = true
    self.collision.collisionDelegate = self
    self.animator.addBehavior(self.collision)
    let itemBehaviour = UIDynamicItemBehavior(items: [self.box])
    itemBehaviour.elasticity = 0.5
    self.animator.addBehavior(itemBehaviour)
}
//碰撞行为协议
internal func collisionBehavior(behavior: UICollisionBehavior, beganContactForItem item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, atPoint p: CGPoint){
    //吸附行为
    self.attach = UIAttachmentBehavior(item: self.attachmentPoint, attachedToItem: self.box)
    self.animator.addBehavior(self.attach)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

}```

4.推行为(UIPushBehavior)

推行为可以使视图对象朝某个方向运动,这个推力有瞬间(UIPushBehaviorModeInstantaneous)(UIPushBehaviorModeContinuous)两种。效果图如下:

IOS动画技术详解_第3张图片
push.gif
import UIKit

class UIPushViewController: UIViewController,UICollisionBehaviorDelegate {

    @IBOutlet weak var barrier: UIImageView!
    @IBOutlet weak var box: UIImageView!
    @IBOutlet weak var attachmentPoint: UIImageView!
    var firstConcact = false
    
    var collision : UICollisionBehavior!
    var animator : UIDynamicAnimator!
    var gravity : UIGravityBehavior!
    var attach : UIAttachmentBehavior!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "推行为"
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.animator = UIDynamicAnimator(referenceView: self.view)
        self.gravity = UIGravityBehavior(items: [self.box])
        self.animator.addBehavior(self.gravity)
        
        self.collision = UICollisionBehavior(items: [self.box])
        let width = self.barrier.frame.size.width
        let origin = self.barrier.frame.origin
        self.collision.addBoundaryWithIdentifier("barrier", fromPoint: origin, toPoint: CGPointMake(origin.x+width, origin.y))
        self.collision.translatesReferenceBoundsIntoBoundary = true
        self.collision.collisionDelegate = self
        self.animator.addBehavior(self.collision)
        let itemBehaviour = UIDynamicItemBehavior(items: [self.box])
        itemBehaviour.elasticity = 0.5
        self.animator.addBehavior(itemBehaviour)
    }
    
    internal func collisionBehavior(behavior: UICollisionBehavior, beganContactForItem item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, atPoint p: CGPoint){
        if !self.firstConcact {
        self.firstConcact = true
        
        self.attach = UIAttachmentBehavior(item: self.attachmentPoint, attachedToItem: self.box)
        self.animator.addBehavior(self.attach)
        //设置推行为
        let push = UIPushBehavior(items: [self.box],mode: UIPushBehaviorMode.Instantaneous)
        //设置推行为的方向和力度
        push.setAngle(CGFloat(-M_PI/4.0), magnitude: 5.0)
        self.animator.addBehavior(push)
        
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}```
#####5.甩行为(UISnapBehavior)
能够使物体朝某个方向甩出,并有瞬间加速度,由慢及快,再由快及慢,最后停止在目标点。效果图如下:

![snap.gif](http://upload-images.jianshu.io/upload_images/1746439-741dc692417379f9.gif?imageMogr2/auto-orient/strip)

import UIKit

class UISnapViewController: UIViewController {
var animator : UIDynamicAnimator!
var snap : UISnapBehavior!

@IBOutlet weak var box: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()
    box.userInteractionEnabled = true;
    box.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(UISnapViewController.snapClick(_:))))
      self.title = "甩行为"
}

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)
    self.animator = UIDynamicAnimator(referenceView: self.view)
   
}

func snapClick(sender: AnyObject) {
print("甩")
let gesture = sender as! UITapGestureRecognizer
let point = gesture.locationInView(self.view)
if self.snap != nil {
self.animator.removeBehavior(self.snap)
}
self.snap = UISnapBehavior(item: self.box, snapToPoint: point)
self.animator.addBehavior(self.snap)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

}```

6.行为限制(DynamicItemBehavior)

行为限制于上面五个行为不同,它是用来设置力学行为参数的。参数如下:
(1)density:密度,如果一个100x100点的物体密度是1.0作用力是1.0那么它的加速度是100点/s^2
(2)elasity:弹性系数,取值范围0.0-1.0。其中0.0代表没有反弹,1.0代表完全弹性碰撞。
(3)friction:摩擦系数,0.0表示没有摩擦力,1.0表示摩擦力很强。还可以设置超过1.0的跟大的摩擦力。
(4)resistance:阻力,0.0表示没有阻力,CGFLOAT_MAX表示最大阻力。
(5)allowsRotation:是否允许旋转。
(6)angularResistance:角阻力,物体旋转是旋转方向的阻力。
我们来看看DynamicItemBehavior具体怎么使用吧:

 let itemBehaviour = UIDynamicItemBehavior(items:[self.view])
        itemBehaviour.elasticity = 0.5
        itemBehaviour.allowsRotation = true
        itemBehaviour.friction = 0.0
        itemBehaviour.resistance = 0.0
        self.animator.addBehavior(itemBehaviour)

Core Animation框架

(1)隐式动画是一种最简单动画,不用设置定时器,不用考虑线程或者重画,它的很多属性都是默认的。
(2)显式动画是一种使用CABasicAnimation创建的动画通过CABasicAnimation,可以更明确的定义属性如何改变动画。
动画效果如下:


IOS动画技术详解_第4张图片
隐式动画
import UIKit

class ExplicitViewController: UIViewController {
    @IBOutlet weak var plane: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.plane.layer.opacity = 0.25
    }

    //不透明度从0.25-1.0
    @IBAction func implicitClick(sender: AnyObject) {
        let moveTransform = CGAffineTransformMakeTranslation(180, 200)
        self.plane.layer.setAffineTransform(moveTransform)
        self.plane.layer.opacity = 1;
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}```
#####显式动画

import UIKit

class ExplicitViewController: UIViewController {
@IBOutlet weak var plane: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()
    self.plane.layer.opacity = 0.25
}

@IBAction func explicitClick(sender: AnyObject) {
let opAnim = CABasicAnimation(keyPath: "opacity")
//定义动画时间
opAnim.duration = 3.0
//将透明的从0.25-1.0
opAnim.fromValue = 0.25
opAnim.toValue = 1.0
//指定累计上次值
opAnim.cumulative = true
//动画重复2次
opAnim.repeatCount = 2
//保持动画结束值
opAnim.fillMode = kCAFillModeForwards
//动画结束时不停止
opAnim.removedOnCompletion = false
self.plane.layer.addAnimation(opAnim, forKey: "animateOpacity")

    let moveTransform = CGAffineTransformMakeTranslation(180, 200)
    let moveAnim = CABasicAnimation(keyPath: "transform")
    moveAnim.duration = 6.0
    moveAnim.toValue = NSValue(CATransform3D: CATransform3DMakeAffineTransform(moveTransform))
    moveAnim.fillMode = kCAFillModeForwards
    moveAnim.removedOnCompletion = false
    self.plane.layer.addAnimation(moveAnim, forKey: "animateTransform")
    opAnim.fillMode = kCAFillModeForwards
    opAnim.removedOnCompletion = false
    
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

}```

关键帧动画
import UIKit

class ExplicitViewController: UIViewController {
    @IBOutlet weak var plane: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.plane.layer.opacity = 0.25
    }

 @IBAction func keyFrameClick(sender: AnyObject) {
        let opAnim = CAKeyframeAnimation(keyPath: "opacity")
        opAnim.duration = 6.0
        opAnim.values = [0.25,0.75,1.0]
        opAnim.keyTimes = [0.0,0.5,1.0]
        opAnim.fillMode = kCAFillModeForwards
        opAnim.removedOnCompletion = false
        self.plane.layer.addAnimation(opAnim, forKey: "animateOpacity")
        
        let moveTransform = CGAffineTransformMakeTranslation(180, 200)
        let moveAnim = CABasicAnimation(keyPath: "transform")
        moveAnim.duration = 6.0
        moveAnim.toValue = NSValue(CATransform3D: CATransform3DMakeAffineTransform(moveTransform))
        moveAnim.fillMode = kCAFillModeForwards
        moveAnim.removedOnCompletion = false
        self.plane.layer.addAnimation(moveAnim, forKey: "animateTransform")
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}```
#####使用路径
效果图如下:

![usePath.gif](http://upload-images.jianshu.io/upload_images/1746439-bdfb4ecc4f660fe4.gif?imageMogr2/auto-orient/strip)

import UIKit

class PathViewController: UIViewController {
@IBOutlet weak var ball: UIImageView!
var flag = 1
@IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}

@IBAction func click(sender: AnyObject) {
    self.button.alpha = 0.0
    //创建可变路径
    let startPath = CGPathCreateMutable()
    CGPathMoveToPoint(startPath, nil, 160.0, 100.0)
    CGPathAddLineToPoint(startPath, nil, 100.0, 280.0)
    CGPathAddLineToPoint(startPath, nil, 260.0, 170.0)
     CGPathAddLineToPoint(startPath, nil, 60.0, 170.0)
    CGPathAddLineToPoint(startPath, nil, 220.0, 280.0)
     //封闭子路径
    CGPathCloseSubpath(startPath)
    
    let animation = CAKeyframeAnimation(keyPath: "position")
    animation.duration = 5.0
     //只有关键帧动画才有setPath方法
    animation.path = startPath
    animation.delegate = self
    self.ball.layer.addAnimation(animation, forKey: "position")

}

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
    UIView.animateWithDuration(1.0) {
        self.button.alpha = 1.0
    }
}
override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

}

所有的代码实例我都建在一个git工程里面,https://git.oschina.net/2276282419/Animator需要的朋友可以下载源代码看看。

你可能感兴趣的:(IOS动画技术详解)