iOS 动画总结

作者: 克理斯 | 来源:发表于2018-11-12 09:49 被阅读10次

    iOS 动画

    Git代码地址

    enter image description here

    在iOS实际开发中常用的动画总结下来包含3种:

    • UIViewAnimation动画
    • CoreAnimation核心动画
    • 其他动画

    [TOC]

    UIViewAnimation动画

    UIView的两种动画api包含 方法形式block 形式, 其中包含三种动画实现

    • UIView(UIViewAnimation)
    • UIView (UIViewKeyframeAnimation)
    • UIViewControllerAnimatedTransitioning

    函数形式

    // 开始动画
    UIView.beginAnimations("Identifier", context: nil)
    //  设置动画代理
    UIView.setAnimationDelegate(self)
    // 通过 #selector 选择器 添加开始动画方法
    UIView.setAnimationWillStart(#selector(animationAction))
    // 通过 #selector 选择器 添加结束动画方法
    UIView.setAnimationDidStop(#selector(animationAction))
    // 设置动画时间间隔
    UIView.setAnimationDuration(1.0)
    // 设置动画延迟
    UIView.setAnimationDelay(0)
    // 设置动画开始的时间,默认是现在开始
    UIView.setAnimationStart(Date())
    // 设置动画曲线
    UIView.setAnimationCurve(.easeInOut)
    // 设置动画重复次数,默认是 0
    UIView.setAnimationRepeatCount(0) // 0 无线循环
    // 自动返回原始状态
    UIView.setAnimationRepeatAutoreverses(false) // default = NO. used if repeat count is non-zero
    // 设置动画的开始是从现在的状态开始, 默认是 false
    UIView.setAnimationBeginsFromCurrentState(false)
    // 用来开启或禁止动画显示
    UIView.setAnimationsEnabled(true)
    // 设置动画的过渡效果
    UIView.setAnimationTransition(.curlUp, for: redView, cache: false)
    
    // 设置 UIView 的动画属性
    redView.transform = CGAffineTransform(rotationAngle: 90)
    redView.transform = CGAffineTransform(scaleX: 0.3, y: 0.3)
    redView.transform = CGAffineTransform(translationX: 0, y: 200)
    
    // 动画的状态
    print(UIView.areAnimationsEnabled)
    // 标志动画代码结束,程序会创建新的线程,并准备运行动画
    UIView.commitAnimations()
    
    UIView.beginAnimations("Identifier", context: nil)
    UIView.setAnimationDuration(1)
    objectView.center.x = objectView.center.x + 100
    UIView.commitAnimations()
    

    block形式

    将动画实现封装在block区域,参数构建在类方法上。

    UIView.animate(withDuration: TimeInterval,
                     animations: ()->Void)
    
    UIView.animate(withDuration: TimeInterval,
                     animations: ()->Void,
                     completion: ()->Void)
    
    // 带动画曲线动画
    UIView.animate(withDuration: TimeInterval,
                          delay: TimeInterval,
                        options: UIViewAnimationOptions,
                     animations: ()->Void,
                     completion: (()->Void)?)
    
    // 带弹性动画
    UIView.animate(withDuration: TimeInterval,
                          delay: TimeInterval,
         usingSpringWithDamping: 0,
          initialSpringVelocity: 0,
                        options: UIViewAnimationOptions,
                     animations: ()->Void,
                     completion: (()->Void)?)
    
    UIView.animate(withDuration: 1.0) {
        self.objectView.center.x = self.objectView.center.x - 100
    }
    

    更多参数动画函数

    //.curveEaseIn, .autoreverse, .repeat
     UIView.animate(withDuration: 1.0, delay: 0, options: [.curveEaseIn, .autoreverse, .repeat], animations: {
         self.objectView.center.x = self.objectView.center.x - 100
     }, completion: nil)
     
     //delay
     UIView.animate(withDuration: 1.0, delay: 0.5, options: .curveEaseIn, animations: {
         self.objectView2.center.x = self.objectView2.center.x - 100
     }, completion: nil)
    

    各个参数的含义

    Duration  :动画执行的时长
    delay     :延时时长,即上一个动画执行完以后多久再执行下一个动画
    options   :一些样式的选取
    animations:我们想要实现的动画效果
    completion:动画执行完我们还想做的事情
    

    options

    1.属性设置

    UIViewAnimationOptionLayoutSubviews // 动画过程中保证子视图跟随运动 
    UIViewAnimationOptionAllowUserInteraction   // 动画过程中允许用户交互  
    UIViewAnimationOptionBeginFromCurrentState    // 所有视图从当前状态开始运行
    UIViewAnimationOptionRepeat  // 重复运行动画                
    UIViewAnimationOptionAutoreverse  // 动画运行到结束点后仍然以动画方式回到初始点       
    UIViewAnimationOptionOverrideInheritedDuration // 忽略嵌套动画时间设置
    UIViewAnimationOptionOverrideInheritedCurve   // 忽略嵌套动画速度设置 
    UIViewAnimationOptionAllowAnimatedContent  // 动画过程中重绘视图(注意仅仅适用于转场动画)    
    UIViewAnimationOptionShowHideTransitionViews // 视图切换时直接隐藏旧视图、显示新视图,而不是将旧视图从父视图移除(仅仅适用于转场动画)  
    UIViewAnimationOptionOverrideInheritedOptions //不继承父动画设置或动画类型
    

    2.动画速度控制

    UIViewAnimationOptionCurveEaseInOut   // 动画先缓慢,然后逐渐加速
    UIViewAnimationOptionCurveEaseIn    // 动画逐渐变慢        
    UIViewAnimationOptionCurveEaseOut   // 动画逐渐加速       
    UIViewAnimationOptionCurveLinear    // 动画匀速执行,默认值
    

    3.转场类型(仅适用于转场动画设置,可以从中选择一个进行设置,基本动画、关键帧动画不需要设置)

    UIViewAnimationOptionTransitionNone  // 没有转场动画效果        
    UIViewAnimationOptionTransitionFlipFromLeft  // 从左侧翻转效果 
    UIViewAnimationOptionTransitionFlipFromRight  // 从右侧翻转效果
    UIViewAnimationOptionTransitionCurlUp // 向后翻页的动画过渡效果        
    UIViewAnimationOptionTransitionCurlDown  // 向前翻页的动画过渡效果      
    UIViewAnimationOptionTransitionCrossDissolve  // 旧视图溶解消失显示下一个新视图的效果 
    UIViewAnimationOptionTransitionFlipFromTop  // 从上方翻转效果   
    UIViewAnimationOptionTransitionFlipFromBottom // 从底部翻转效果
    

    支持的动画属性

    frame //大小变化:改变视图框架(frame)和边界。
    bounds //拉伸变化:改变视图内容的延展区域。
    center //居中显示
    transform //仿射变换(transform)
    alpha //改变透明度:改变视图的alpha值。
    backgroundColor //改变背景颜色
    contentStretch //拉伸内容
    
    • Opacity -- 设置透明度
    UIView.animate(withDuration: 1.0) {
        self.objectView.alpha = 0.2
    }
    
    • Scale -- 放大缩小
    UIView.animate(withDuration: 1.0) {
        self.objectView.transform = CGAffineTransform(scaleX: 2, y: 2)
    }
    
    UIView.animate(withDuration: 1.0, delay: 1.0, options: .curveLinear, animations: {
        self.objectView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
    }, completion: nil)
    
    • Translation -- 平移
    UIView.animate(withDuration: 1.0) {
        self.objectView.transform = CGAffineTransform(translationX: 200, y: 200)
    }
    
    • Color -- 颜色
    UIView.animate(withDuration: 1.0, delay: 0, options: [.autoreverse, .repeat], animations: {
        self.objectView.backgroundColor = UIColor.brown
    }, completion: nil)
    
    • Rotation -- 旋转
    UIView.animate(withDuration: 1.0, delay: 0, options: [.curveLinear, .repeat], animations: {
        self.objectView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi))
    }, completion: nil)
    

    弹簧动画函数

    UIView.animate(withDuration: 1.0, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 20, options: .curveEaseIn, animations: {
        self.objectView.center.y = self.objectView.center.y + 100
    }, completion:nil)
    

    关键帧动画

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

    UIViewKeyframeAnimationOptions:

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

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

    frameStartTime:帧动画开始时间,取值范围为(0,1),开始时间是相对于整个动画时间,整个关键帧动画时长6秒,设置开始时间为0.5,那么这一帧动画的实际开始时间为第3秒!
    2 frameDuration:帧动画持续时间,取值范围为(0,1),持续时间也是相对于整个动画时间,算法同上!
    
    UIView.animateKeyframes(withDuration: 10, delay: 0, options: .calculationModeCubicPaced, animations: {
         UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 1/5, animations: {
             self.objectView.backgroundColor = UIColor.red
         })
         UIView.addKeyframe(withRelativeStartTime: 1/5, relativeDuration: 1/5, animations: {
             self.objectView.backgroundColor = UIColor.green
         })
         UIView.addKeyframe(withRelativeStartTime: 2/5, relativeDuration: 1/5, animations: {
             self.objectView.backgroundColor = UIColor.yellow
         })
         UIView.addKeyframe(withRelativeStartTime: 3/5, relativeDuration: 1/5, animations: {
             self.objectView.backgroundColor = UIColor.purple
         })
         UIView.addKeyframe(withRelativeStartTime: 4/5, relativeDuration: 1/5, animations: {
             self.objectView.backgroundColor = UIColor.gray
         })
     }, completion: nil)
    

    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
    
    if self.objectView.backgroundColor == UIColor.gray {
        self.objectView.backgroundColor = UIColor.blue
    }else {
        self.objectView.backgroundColor = UIColor.gray
    }
    }, completion: nil)
    

    ImageView动画

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

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

    直接指定了image数组和duration。

    UIImage.animatedImage(with:, duration:)
    

    提供一个单个的image name , 系统会自动在后面加 "0" (如果失败则"1") 。使这个image成为第一个image。最后一位数字累加。(知道没有图片或者到达”1024“)

    UIImage.animatedImageNamed(, duration: )
    

    跟上面的方式差不多,但是同时对每个image做了拉伸或者平铺。 图像本身也有resizableImage(withCapInsets: , resizingMode: )方法可以缩放(指定某个区域的拉伸或者平铺)

    UIImage.animatedResizableImageNamed(, capInsets: , duration: )
    
    let image = UIImage.animatedImageNamed("voice", duration: 2)
    self.imageView.image = image
    

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

    CoreAnimation核心动画

    Core Animation可以用在 Mac OS X 和 iOS平台. Core Animation的动画执行过程是在后台操作的.不会阻塞主线程. 要注意的是, Core Animation是直接作用在CALayer上的.并非UIView

    • CABasicAnimation 基础动画
    • CAKeyframeAnimation 关键帧动画
    • CATransition 转场动画
    • CAAnimationGroup 组动画
    • CASpringAnimation 弹性动画 (iOS9.0之后新增CASpringAnimation类,它实现弹簧效果的动画,是CABasicAnimation的子类。)

    动画操作过程:

    • 创建一个CAAnimation对象
    • 设置一些动画的相关属性
    • 给CALayer添加动画(addAnimation:forKey: 方法)
    • 移除CALayer中得动画(removeAnimationForKey: 方法)

    CAAnimation (一部分属性来自 CAMediaTiming)

    enter image description here
    1. duration:动画的持续时间,默认为0.25秒

    2. speed :速度 speed = 1.0 / duration = 1.0 的动画效果 和 speed = 2.0 / duration = 2.0 的动画效果是一模一样的,我们设置的duration可能和动画进行的真实duration不一样,这个还依赖于speed。

    3. timeOffset 设置动画线的起始结束时间点

    //假定一个3s的动画,它的状态为t0,t1,t2,t3,当没有timeOffset的时候,正常的状态序列应该为:
    //t0->t1->t2->t3
    //当设置timeOffset为1的时候状态序列就变为
    //t1->t2->t3->t0
    //同理当timeOffset为2的时候状态序列就变为:
    //t2->t3->t0->t1
    
    1. autoreverses:是否自动回到动画开始状态

    2. repeatCount:动画的重复次数

    3. repeatDuration:动画的重复时间

    4. removedOnCompletion:默认为YES,代表动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。如果想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillMode属性为kCAFillModeForwards

    5. fillMode:决定当前对象在非active时间段的行为.比如动画开始之前,动画结束之后

    6. beginTime:可以用来设置动画延迟执行时间,若想延迟2s,就设置为CACurrentMediaTime()+2,CACurrentMediaTime()为图层的当前时间。 CALayer 的beginTime 一般用于动画暂停的使用,CAAnimation 的beginTime一般用于动画延迟执行,但只在使用groupAnimation的时候生效,直接添加在layer上的animation使用会导致动画不执行。

    7. timingFunction:速度控制函数,控制动画运行的节奏

    枚举参数:

    kCAMediaTimingFunctionLinear  时间曲线函数,匀速
    kCAMediaTimingFunctionEaseIn  时间曲线函数,由慢到特别快
    kCAMediaTimingFunctionEaseOut  时间曲线函数,由快到慢
    kCAMediaTimingFunctionEaseInEaseOut  时间曲线函数,由慢到快
    kCAMediaTimingFunctionDefault   系统默认
    
    1. delegate:动画代理,一般设置隐式代理,该代理是NSObject的分类,需要遵守协议CAAnimationDelegate
    -(void)animationDidStart:(CAAnimation *)anim; 核心动画开始时执行
    
    -(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; 核心动画执行结束后调用
    

    CAPropertyAnimation

    属性:

    keyPath:通过指定CALayer的一个属性名做为keyPath里的参数(NSString类型),并且对CALayer的这个属性的值进行修改,达到相应的动画效果。比如,指定@”position”为keyPath,就修改CALayer的position属性的值,以达到平移的动画效果。

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
    

    一些常用的animationWithKeyPath值的总结

    说明 使用形式
    transform.scale 比例转化 @(0.8)
    transform.scale.x 宽的比例 @(0.8)
    transform.scale.y 高的比例 @(0.8)
    transform.rotation.x 围绕x轴旋转 @(M_PI)
    transform.rotation.y 围绕y轴旋转 @(M_PI)
    transform.rotation.z 围绕z轴旋转 @(M_PI)
    cornerRadius 圆角的设置 @(50)
    backgroundColor 背景颜色的变化 (id)[UIColor purpleColor].CGColor
    bounds 大小,中心不变 [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)];
    position 位置(中心点的改变) [NSValue valueWithCGPoint:CGPointMake(300, 300)];
    contents 内容,比如UIImageView的图片 imageAnima.toValue = (id)[UIImage imageNamed:@"to"].CGImage;
    opacity 透明度 @(0.7)
    contentsRect.size.width 横向拉伸缩放 @(0.4)最好是0~1之间的

    CABasicAnimation基本动画

    CABasicAnimation能实现诸多的动画,移动,旋转,缩放....KeyPath所涉及的都能实现

    1.fromValue : keyPath相应属性的初始值
    **2.toValue **: keyPath相应属性的结束值,到某个固定的值(类似transform的make含义)
    注意:随着动画的进行,在长度为duration的持续时间内,keyPath相应属性的值从fromValue渐渐地变为toValue.
    如果fillMode = kCAFillModeForwards和removedOnComletion = NO;那么在动画执行完毕后,图层会保持显示动画执行后的状态,但实质上,图层的属性值还是动画执行前的初始值,并没有真正被改变.比如: CALayer的postion初始值为(0,0),CABasicAnimation的fromValue为(10,10),toValue为 (100,100),虽然动画执行完毕后图层保持在(100,100) 这个位置,实质上图层的position还是为(0,0);
    3.byValue:不断进行累加的数值(byvalue 值加上fromValue => tovalue)

    Position

    let baseAnimation = CABasicAnimation(keyPath: "position.x")
    baseAnimation.fromValue = objectView.center.x
    baseAnimation.toValue = objectView.center.x + 100
    baseAnimation.duration = 1
    //逆行动画
    baseAnimation.autoreverses = true
    baseAnimation.repeatCount = MAXFLOAT
    
    //防止动画接收后回到初始状态
    baseAnimation.isRemovedOnCompletion = false
    baseAnimation.fillMode = CAMediaTimingFillMode.forwards
    
    objectView.layer.add(baseAnimation, forKey: "demo")
    

    Scale

    let baseAnimation = CABasicAnimation(keyPath: "transform.scale")
    baseAnimation.fromValue = 0.5
    baseAnimation.toValue = 1
    baseAnimation.duration = 1
    baseAnimation.repeatCount = MAXFLOAT
    baseAnimation.fillMode = CAMediaTimingFillMode.forwards
    objectView.layer.add(baseAnimation, forKey: "demo")
    

    CASpringAnimation弹性动画

    iOS9才引入的动画类,它继承于CABaseAnimation,用于制作弹簧动画

    参数说明:

    mass:
    质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大

    stiffness:
    刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快

    damping:
    阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快

    initialVelocity:
    初始速率,动画视图的初始速度大小
    速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反

    settlingDuration:
    结算时间 返回弹簧动画到停止时的估算时间,根据当前的动画参数估算
    通常弹簧动画的时间使用结算时间比较准确

    let springAnimation = CASpringAnimation(keyPath: "position.x")
    springAnimation.damping = 5
    springAnimation.stiffness = 100;
    springAnimation.mass = 1;
    springAnimation.initialVelocity = 0;
    springAnimation.fromValue = objectView.layer.position.x;
    springAnimation.toValue = objectView.layer.position.x + 50;
    springAnimation.duration = springAnimation.settlingDuration;
    objectView.layer.add(springAnimation, forKey: springAnimation.keyPath);
    

    CAKeyframeAnimation关键帧动画

    • CAKeyframeAnimation跟CABasicAnimation的区别是:
      CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值.
    • CAKeyframeAnimation属性解析:
      values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧 .
      path:如果你设置了path,那么values将被忽略.
      keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的.
      注: CABasicAnimation能实现的CAKeyframeAnimation也能实现,而且更具体和准确

    Shake Sample

    let shakeAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
    //设置晃动角度
    let angle = Double.pi / 2
    //设置关键帧动画的值
    shakeAnimation.values = [angle, -angle, angle]
    //设置关键帧动画每帧的执行时间,这里不设置也行,默认平均分配时间
    shakeAnimation.keyTimes = [0, 0.5, 1]
    //设置动画重复次数,默认为1次
    shakeAnimation.repeatCount = MAXFLOAT
    //设置动画执行效果
    shakeAnimation.timingFunctions = [CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)]
    //设置相邻动画过渡方式
    shakeAnimation.calculationMode = CAAnimationCalculationMode.cubic
    objectView.layer.add(shakeAnimation, forKey: shakeAnimation.keyPath);
    

    轨迹动画

    let path = UIBezierPath()
    //设置动画的执行路径为一个M的形状
    path.move(to: CGPoint(x: 40, y: 300))
    path.addLine(to: CGPoint(x: 80, y: 150))
    path.addLine(to: CGPoint(x: 120, y: 300))
    path.addLine(to: CGPoint(x: 160, y: 150))
    path.addLine(to: CGPoint(x: 200, y: 300))
    let bezierAnimation = CAKeyframeAnimation(keyPath: "position")
    //由于CAKeyframeAnimation的path为CGPath,所以这里要转换一次
    bezierAnimation.path = path.cgPath
    //设置动画时间
    bezierAnimation.duration = 4
    //自动旋转layer角度与path相切
    bezierAnimation.rotationMode = CAAnimationRotationMode.rotateAuto
    //设置动画重复次数
    bezierAnimation.repeatCount = MAXFLOAT
    //设置自动逆向
    bezierAnimation.autoreverses = true
    objectView.layer.add(bezierAnimation, forKey: nil)
    

    Scale动画

    let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
    scaleAnimation.values = [0.0, 0.4, 0.8, 1.2, 1.6, 1.2, 0.8, 0.4, 0.0]
    scaleAnimation.duration = 2
    scaleAnimation.autoreverses = true
    scaleAnimation.repeatCount = MAXFLOAT
    objectView.layer.add(scaleAnimation, forKey: nil)
    

    CAAnimationGroup组合动画

    将多个动画组合和并发运行
    delegate 和 isRemovedOnCompletion 在动画的属性数组中目前被忽略。
    CAAnimationGroup 的 delegate 接收这些消息

    • animations CAAnimation 数组,用于添加多个 CAAnimation 动画
    let animationPath = CAKeyframeAnimation.init(keyPath: "position")
    animationPath.path = path.cgPath
    animationPath.rotationMode = CAAnimationRotationMode.rotateAuto
    
    //旋转
    let rotate:CABasicAnimation = CABasicAnimation()
    rotate.keyPath = "transform.rotation"
    rotate.toValue = Double.pi
    
    //缩小图片到0
    let scale:CABasicAnimation = CABasicAnimation()
    scale.keyPath = "transform.scale"
    scale.toValue = 0.0
    
    //组合动画
    let animationGroup:CAAnimationGroup = CAAnimationGroup()
    animationGroup.animations = [animationPath,rotate,scale];
    animationGroup.duration = 2.0;
    animationGroup.fillMode = CAMediaTimingFillMode.forwards;
    animationGroup.isRemovedOnCompletion = false
    objectView.layer.add(animationGroup, forKey:
        nil)
    

    CATransition转场动画

    CAAnimation的子类
    在图层状态之间提供动画转换的对象
    提供了一个图层之间的过渡的动画

    CATransition 有一个 type 和 subtype 来标识变换效果

    新增加的属性

    • startProgress 开始的进度 0~1
    • endProgress 结束时的进度 0~1
    • type 转换类型
      • kCATransitionFade (default)
      • kCATransitionMoveIn
      • kCATransitionPush
      • kCATransitionReveal
    • API引入的type,在苹果官网是不会承认的,所以不建议使用
      • 1 animation.type = @"cube"; //立方体效果
      • 2 animation.type = @"suckEffect";//犹如一块布被抽走
      • 3 animation.type = @"oglFlip"; //上下翻转效果
      • 4 animation.type = @"rippleEffect"; //滴水效果
      • 5 animation.type = @"pageCurl"; //向左翻页
      • 6 animation.type = @"pageUnCurl"; //向下翻页
    • subtype 基于运动方向预定义的转换
      • kCATransitionFromLeft
      • kCATransitionFromRight
      • kCATransitionFromTop
      • kCATransitionFromBottom
    • filter 滤镜

    ** View Transaition**

    let animation = CATransition()
    animation.duration = 1.0
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    // `fade', `moveIn', `push' and `reveal'. Defaults to `fade'
    animation.type = CATransitionType.reveal
    // `fromLeft', `fromRight', `fromTop' and `fromBottom'
    animation.subtype = CATransitionSubtype.fromLeft
    //        animation.isRemovedOnCompletion = true
    animation.startProgress = 0.5
    objectView.layer.add(animation, forKey: nil)
    

    ** ViewController Transaition(翻页效果)**

    let vc = UIStoryboard(name: "Main", bundle: nil)
        .instantiateViewController(withIdentifier: "second")
    
    let anima = CATransition.init()
    //        anima.type = CATransitionType.reveal
    anima.type = CATransitionType(rawValue: "pageUnCurl")
    anima.subtype = CATransitionSubtype.fromLeft
    anima.duration = 1.0
    
    UIApplication.shared.keyWindow?.layer.add(anima, forKey: "pageUnCurl")
    //        UIApplication.shared.keyWindow?.layer.removeAnimation(forKey: "pageUnCurl")
    self.navigationController?.pushViewController(vc, animated: false)
    

    其他动画

    控制器转场动画 UIViewControllerAnimatedTransitioning

    一、 转场动画-modal

    给vc的transitioningDelegate属性赋值,为即将跳转的vc指定转场动画代理,协议中有两个基础方法,分别要求代理返回present时的动画以及dismiss时的动画。

    class FadeTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate  {
        private lazy var fadeAnimator = FadeAnimator()
        
        // 提供dismiss的时候使用到的动画执行对象
        func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            fadeAnimator.isPresenting = false
            return fadeAnimator
        }
        
        // 提供present的时候使用到的动画执行对象
        func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            fadeAnimator.isPresenting = true
            return fadeAnimator
        }
    }
    

    写一个类专门来实现UIViewControllerAnimatedTransitioning动画协议,作为动画的实现类。然后代理就可以返回两个动画实现对象,实现UIViewControllerTransitioningDelegate转场代理协议。

    获取转场过程的三个视图:containerView、fromView、toView。
    containerView是动画过程中提供的暂时容器。
    fromView是转场开始页的视图。
    toView是转场结束页的视图。

    转场的过程,大多数情况下我们都是对toView作各种变换操作,例如改变toView的alpha,size,旋转等等。 在对它进行操作前,需要先把它放到container上才能显示出来。[container addSubview:toView];

    class FadeAnimator: NSObject, UIViewControllerAnimatedTransitioning {
        
        let duration = 1.0
        var isPresenting = true
        
        // 指定转场动画持续的时间
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return duration
        }
        
        // 实现转场动画的具体内容
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            // 得到容器视图
            let containerView = transitionContext.containerView
            // 目标视图
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
            
            containerView.addSubview(toView)
            
            // 为目标视图的展现添加动画
            toView.alpha = 0.0
            UIView.animate(withDuration: duration,
                           animations: {
                            toView.alpha = 1.0
            }, completion: { _ in
                transitionContext.completeTransition(true)
            })
        }
    }
    

    最后presentViewController的时候赋值代理

    let transitionDelegate = FadeTransitionDelegate()
     @IBAction func UIViewControllerAnimatedTransitioning_Demo(_ sender: Any) {
         let vc = UIStoryboard(name: "Main", bundle: nil)
             .instantiateViewController(withIdentifier: "second")
         
         vc.transitioningDelegate = transitionDelegate
         present(vc, animated: true, completion: nil)
     }
    

    二、 转场动画-push

    流程同 转场动画基础用法-modal
    要自定义push动画,需实现导航控制器的代理协议 UINavigationControllerDelegate

    class PushTansitionDelegate: NSObject, UINavigationControllerDelegate {
        private lazy var fadeAnimator = FadeAnimator()
        // 是否需要交互
        var interactive = false
        
        //返回一个不可交互的转场动画
        func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return fadeAnimator
        }
    }
    

    其中fadeAnimator和Model里的是同样的定义。
    使用Push动画

    let vc = UIStoryboard(name: "Main", bundle: nil)
        .instantiateViewController(withIdentifier: "second")
    
    self.navigationController?.delegate = pushTransitionDelegate
    self.navigationController?.pushViewController(vc, animated: true)
    

    一、 转场动画-交互式转场动画

    交互式过渡是由事件驱动的。可以是动作事件或者手势,通常为手势。要实现一个交互式过渡,除了需要跟之前相同的动画,还需要告诉交互控制器动画完成了多少。开发者只需要确定已经完成的百分比,其他交给系统去做就可以了。例如,(平移和缩放的距离 / 速度的量可以作为计算完成的百分比的参数)。

    交互式控制器实现了 UIViewControllerInteractiveTransitioning 协议:

    - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
    

    这个方法里只能有一个动画块,动画应该基于 UIView 而不是图层,交互式过渡不支持 CATransition 或 CALayer 动画。

    交互式过渡的交互控制器应当是 UIPercentDrivenInteractiveTransition 子类。动画类负责计算完成百分比,系统会自动更新动画的中间状态。

    - (void)updateInteractiveTransition:(CGFloat)percentComplete;
    - (void)cancelInteractiveTransition;
    - (void)finishInteractiveTransition;
    

    对FadeAnimator添加handlePan,并且继承UIPercentDrivenInteractiveTransition 支持交互式动画

    class FadeAnimator: UIPercentDrivenInteractiveTransition, UIViewControllerAnimatedTransitioning {
        
        let durationAnimation = 1.0
        
        //  present/dismiss, push/pop
        var isPresenting = true
        // 是否需要交互
        var interactive = false
        
        // 指定转场动画持续的时间
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return durationAnimation
        }
        
        // 实现转场动画的具体内容
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            // 得到容器视图
            let containerView = transitionContext.containerView
            // 目标视图
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
            
            containerView.addSubview(toView)
            
            // 为目标视图的展现添加动画
            toView.alpha = 0.0
            UIView.animate(withDuration: durationAnimation,
                           animations: {
                            toView.alpha = 1.0
            }, completion: { _ in
                transitionContext.completeTransition(true)
            })
        }
        
        func handlePan(recognizer: UIPanGestureRecognizer) {
            let translation = recognizer.translation(in: recognizer.view!.superview!)
            var progress: CGFloat = abs(translation.x / 200.0)
            progress = min(max(progress, 0.01), 0.99)
            
            switch recognizer.state {
            case .changed:
                // 更新当前转场动画播放进度
                update(progress)
            case .cancelled:
                cancel()
            case .ended:
                finish()
            default:
                break
            }
        }
    

    对应的Delegate更改为 支持交互的转场动画:

    class PushTansitionDelegate: NSObject, UINavigationControllerDelegate {
        private lazy var fadeAnimator = FadeAnimator()
        // 是否需要交互
        var interactive = false
        
        //返回一个不可交互的转场动画
        func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
            return fadeAnimator
        }
        
        // 返回一个可以交互的转场动画
        func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
            if interactive == false {
                return nil
            }
            
            return fadeAnimator
        }
        
    
        func handlePan(recognizer: UIPanGestureRecognizer) {
            fadeAnimator.handlePan(recognizer: recognizer)
        }
    }
    

    控制器中使用,第二个页面返回的时候使用交互式转场动画

    class SecondViewController: UIViewController {
         let pushTransitionDelegate = PushTansitionDelegate()
        
        override func viewDidLoad() {
            super.viewDidLoad()
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            
            let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(recognizer:)))
            self.view.addGestureRecognizer(pan)
        }
        
        @objc func handlePan(recognizer: UIPanGestureRecognizer) {
            if recognizer.state == .began {
                
                pushTransitionDelegate.interactive = true
                self.navigationController?.delegate = pushTransitionDelegate
                self.navigationController?.popViewController(animated: true)
            }else {
                pushTransitionDelegate.handlePan(recognizer: recognizer)
            }
        }
        
        @IBAction func close(_ sender: Any) {
            self.dismiss(animated: true, completion: nil)
        }
    }
    

    UIDynamic-动力学框架

    UIDynamic是苹果在iOS7之后添加的一套动力学框架,运用它我们可以极其方便地模拟现实生活中的运动,比如重力,碰撞等等。它是通过添加行为的方式让动力学元素参与运动的。

    iOS7.0中提供的动力学行为包括:

    UIGravityBehavior:重力行为
    UICollisionBehavior:碰撞行为
    UIAttachmentBehavior:附着行为
    UISnapBehavior:吸附行为
    UIPushBehavior:推行为
    UIDynamicItemBehavior:动力学元素行为

    UIDynamic的使用还是相对简单

    1.首先我们创建一个小方块 objectView 并把它放在self.view的上面部分。(只有遵循了UIDynamicItem协议的对象才能参与仿真模拟,而UIView正遵循了此协议,因此所有视图控件都能参与仿真运动)

    2.然后定义一个 UIDynamicAnimator 物理仿真器(凡是要参与运动的对象必须添加到此容器中)

    3.再添加一个重力行为 到仿真器,并且 这个行为作用对象是我们之前定义的boxView

    4.可以发现 放在self.view上半部分的boxView受重力行为影响,往下掉落。但是会掉出self.view范围。

    5.为了不掉出self.view 范围 我们还需要给objectView添加一个别的行为:碰撞行为,接触到仿真器边界或者其他self.view中得容器会产生碰撞效果。

    6.这样小方块就不会掉出仿真器范围了,同理,其他行为的使用方式和上面一样,一定要添加到仿真器才能生效。

    var animator: UIDynamicAnimator!
    @IBAction func UIDynamic_tap(_ sender: Any) {
        animator = UIDynamicAnimator(referenceView: self.view)
        let behavior = UIGravityBehavior(items: [objectView])
        animator.addBehavior(behavior)
        
        let behaviorCollision = UICollisionBehavior(items: [objectView])
        behaviorCollision.translatesReferenceBoundsIntoBoundary = true
        animator.addBehavior(behaviorCollision)
    }
    

    CAEmitterLayer 粒子动画

    1、CAEmitterLayer。 这个主要是定义粒子原型发射层的形状和发射位置,发射源的尺寸以及发射的模式等。

    2、CAEmitterCell 单个粒子的原型,通常有多个,根据cell的属性和CAEmitterCell的配置,由uikit随机生成,粒子原型的属性包括粒子的图片,颜色,方向,运动,缩放比例和生命周期等。

    这两个类的参数看起来似乎很简单,但这些参数的不同组合配合上相对应图片,则可以实现许多意想不到的动画效果。

    var rainLayer: CAEmitterLayer!
    @IBAction func CAEmitterLayer_tap(_ sender: Any) {
        // 粒子发射图层
        rainLayer = CAEmitterLayer()
        // 发射器形状为线形,默认发射方向向上
        rainLayer.emitterShape = CAEmitterLayerEmitterShape.line
        // 从发射器的轮廓发射粒子
        rainLayer.emitterMode = CAEmitterLayerEmitterMode.outline
        // 优先渲染旧的粒子
        rainLayer.renderMode = CAEmitterLayerRenderMode.oldestFirst
        // 发射位置
        // 对于线形发射器,线的两端点分别为
        // (emitterPosition.x - emitterSize.width/2, emitterPosition.y, emitterZPosition)和
        // (emitterPosition.x + emitterSize.width/2, emitterPosition.y, emitterZPosition)
        rainLayer.emitterPosition = CGPoint(x: view.bounds.midX, y: 0)
        // 发射器大小
        rainLayer.emitterSize = CGSize(width: view.bounds.width, height: 0)
        // 粒子生成速率的倍数,一开始不发射,设置为零
        rainLayer.birthRate = 0
        
        // 发射的粒子
        let cell = CAEmitterCell()
        // 粒子显示的内容,设置CGImage,显示图片
        cell.contents = UIImage(named: "star")?.cgImage
        // 粒子缩放倍数
        cell.scale = 0.1
        // 粒子寿命,单位是秒
        cell.lifetime = 5
        // 粒子生成速率,单位是个/秒,实际显示效果要乘以CAEmitterLayer的birthRate
        cell.birthRate = 1000
        // 粒子速度
        cell.velocity = 500
        // 粒子发射角度,正值表示顺时针方向
        cell.emissionLongitude = CGFloat.pi
        
        // 图层要发射1种粒子
        rainLayer.emitterCells = [cell]
        // 添加粒子发射图层
        view.layer.addSublayer(rainLayer)
        
        
        
        //  粒子生成速率渐变动画
        let birthRateAnimation = CABasicAnimation(keyPath: "birthRate")
        birthRateAnimation.duration = 3
        if rainLayer.birthRate == 0 {
            // 雨变大
            birthRateAnimation.fromValue = 0
            birthRateAnimation.toValue = 1
            rainLayer.birthRate = 1
        } else {
            // 雨变小
            birthRateAnimation.fromValue = 1
            birthRateAnimation.toValue = 0
            rainLayer.birthRate = 0
        }
        // 加入动画
        rainLayer.add(birthRateAnimation, forKey: "birthRate")
    }
    

    参考连接
    https://www.jianshu.com/p/71f2fa270b9c
    https://www.jianshu.com/p/9aead7675221
    https://www.jianshu.com/p/802d47f0f311

    相关文章

      网友评论

        本文标题:iOS 动画总结

        本文链接:https://www.haomeiwen.com/subject/dnwoxqtx.html