能够使用嵌套block块来促成,使用提到的底子的卡通相关类来兑现动画效果

前言

在上一篇认识CoreAnimation中小编介绍了系统的动画库CoreAnimation,使用动画库有成都百货上千功利,这里就不再实行双重叙述。那么本篇将承接上一篇的剧情,使用提到的根底的卡通相关类来贯彻动画效果,效果图放上:

大概上得以观察demo首如若渐变以及形变两种动画,在更早在此以前的稿子,大家就接纳UIView的动画接口达成过千篇一律的卡通片,而这一次将换来CoreAnimation来形成这个干活儿

1.毛玻璃效果 (iOS8过后) 

iOS8从此新加了UIBlurEffect类和UIVisualEffectView类使用起来也非常粗略 

UIBlurEffect类设定毛玻璃效果的品类(3中项目)

图片 1

UIVisualEffectView类在创立即进入UIBlurEffect的类的靶子

图片 2

有关图层

在iOS中,每一个UIView都抱有1个与之绑定的CALayer图层对象,其负责视图内容的绘图与展现。面前者一样,CALayer也负有树状的子图层结构,以及相似的接口方法。CALayer是图层的基类,首要提供了视图展现范围、图层结构接口等质量,大家通过运用它的子类。上边是一段在控制器的界面宗旨添加3个圆形的玛瑙红图层:

override func viewDidLoad() {
    super.viewDidLoad()

    let layer = CAShapeLayer()
    layer.fillColor = UIColor.purpleColor().CGColor
    layer.path = UIBezierPath(arcCenter: CGPoint(x: UIScreen.mainScreen().bounds.width / 2, y: UIScreen.mainScreen().bounds.height / 2), radius: 100, startAngle: 0, endAngle: 2.0*CGFloat(M_PI), clockwise: false).CGPath
    self.view.layer.addSublayer(layer)
}

同样的,每一个CALayer存在二个sublayers的数组属性,大家也能够遍历这么些数组来成功移除子视图之类的操作:

for sublayer in self.view.layer.sublayers! {
    print("\(sublayer)")
    sublayer.removeFromSuperlayer()
}

出于中央动画框架的卡通片都以依照CALayer的图层实行添加贯彻的,所以图层的丰裕移除方法是最常用的不二法门。当然,还有三个addAnimation(anim:forKey:)接口用来给图层添加动画

2.系统自带的uiview的block块

三个不难易行的图纸视图的放大然后稳步磨灭的动画片最终移除视图
可以行使嵌套block块来兑现

图片 3

基本功动画

基础动画CABasicAnimation是最常用来兑现动画效果的卡通片类,其继承自CAAnimation动画基类,为图层动画效果实现了3个keyPath品质,我们通过设置这几个性情来为对应的keyPath属性值执行动画效果。动画类提供了fromValuetoValue五个属性用来安装动画的序曲和完工的值,比如下边一段代码让添加到视图上的暗蓝图层变得透明:

@IBAction func actionToAnimatedLayer(sender: AnyObject) {
    let animation = CABasicAnimation(keyPath: "opacity")
    animation.fromValue = NSNumber(double: 1)
    animation.toValue = NSNumber(double: 0)
    animation.duration = 1
    layer.addAnimation(animation, forKey: nil)
}

地点的代码用动画表现了在1秒内让图层的opacity属性从10的经过。但地点简单看出在动画甘休以往,中蓝的图层没有保持opacity等于0的情形,而是回到了动画最起首的状态。那是干吗呢?

在上一篇中小编提到过在每二个CALayer中存在着模型呈现渲染二种图层树,就是那么些图层树共同作用来成功隐式动画。那么使用基本动画的时候,实际上CABasicAnimation会根据动画时间长度总括出每一帧的卡通属性的值,然后实时提交给呈现树来呈现对应时间点的视图效果,在动画结束时CAAnimation对象会自动从图层上移除。而鉴于在全部动画进程模型树的值没有改变,所以在动画甘休的时候呈现树会再一次从模型树得到图层的性质重新绘制。对此,存在那两种缓解方案:

  • 在完毕动画的时候还要修改opacity,保险模型树的多寡同步
    @IBAction func actionToAnimatedLayer(sender: AnyObject) {
    let animation = CABasicAnimation(keyPath: “opacity”)
    animation.fromValue = NSNumber(double: 1)
    animation.toValue = NSNumber(double: 0)
    animation.fillMode = kCAFillModeForwards
    animation.removedOnCompletion = false
    animation.duration = 1

        layer.addAnimation(animation, forKey: nil)
    }
    
  • 取消CAAnimation的机动移除,并且安装在动画甘休后保持动画的停止状态
    @IBAction func actionToAnimatedLayer(sender: AnyObject) {
    let animation = CABasicAnimation(keyPath: “opacity”)
    animation.fromValue = NSNumber(double: 1)
    animation.toValue = NSNumber(double: 0)
    animation.fillMode = kCAFillModeForwards
    animation.removedOnCompletion = false
    animation.duration = 1
    layer.addAnimation(animation, forKey: nil)
    }

  • 贯彻动画代理方法。综合上面两种办法的操作
    @IBAction func actionToAnimatedLayer(sender: AnyObject) {
    let animation = CABasicAnimation(keyPath: “opacity”)
    animation.fromValue = NSNumber(double: 1)
    animation.toValue = NSNumber(double: 0)
    animation.fillMode = kCAFillModeForwards
    animation.removedOnCompletion = false
    animation.duration = 1

        animation.setValue(layer, forKey: "animatedLayer")
        animation.delegate = self
        layer.addAnimation(animation, forKey: nil)
    }
    
    override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
        if anim is CABasicAnimation {
            let animation = anim as! CABasicAnimation
            if let layer = animation.valueForKey("animatedLayer") as? CALayer {
                layer.setValue(animation.toValue, forKey: animation.keyPath!)
                layer.removeAllAnimations()
            }
        }
    }
    

相相比前三种办法,完结代理然后设置属性的做法有点凌乱且低效的感觉到。但在少数应用场景下,大家须要在动画甘休时移除图层或任何操作,通过达成代理是最好的做法。别的常用的keyPath动画值能够在这里查看

3.CALayer层的动画

CABasicAnimation一般用法通过fromValue和toValue来钦点早先和终止的值

属性Autoreverses
当设为YES时,在它到达指标地后,动画回到原有的值,代替直接跳转到开始的值

Duration 动画的时间长度  repeatCount 动画的重新次数(暗中同意为0)只走三遍。

Speed 动画播放遵照私下认可的快慢播放 私下认可为1.0
假设更改它的值会影响到动画的持续时间。

RemovedOnCompletion 设为YES在内定时间内动画完毕后,动画从层上移除

animationWithKeyPath的值:

transform.scale 缩放 

transform.translation.y 纵向活动

transform.translation.x 横向移动

opacity 透明度

transform.rotation.z 旋转

backgroundColor 背景象变化

+(CABasicAnimation *)opacityForever_Animation:(float)time
//永久闪烁的动画

{

CABasicAnimation *animation=[CABasicAnimation
animationWithKeyPath:@”opacity”];

animation.fromValue=[NSNumber numberWithFloat:1.0];

animation.toValue=[NSNumber numberWithFloat:0.0];

animation.autoreverses=YES;

animation.duration=time;

animation.repeatCount=FLT_MAX;

animation.removedOnCompletion=NO;

animation.fillMode=kCAFillModeForwards;

return animation;

}

+(CABasicAnimation *)opacityTimes_Animation:(float)repeatTimes
durTimes:(float)time; //有闪烁次数的卡通

{

CABasicAnimation *animation=[CABasicAnimation
animationWithKeyPath:@”opacity”];

animation.fromValue=[NSNumber numberWithFloat:1.0];

animation.toValue=[NSNumber numberWithFloat:0.4];

animation.repeatCount=repeatTimes;

animation.duration=time;

animation.removedOnCompletion=NO;

animation.fillMode=kCAFillModeForwards;

animation.timingFunction=[CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseIn];

animation.autoreverses=YES;

return  animation;

}

+(CABasicAnimation *)moveX:(float)time X:(NSNumber *)x //横向运动

{

CABasicAnimation *animation=[CABasicAnimation
animationWithKeyPath:@”transform.translation.x”];

animation.toValue=x;

animation.duration=time;

animation.removedOnCompletion=NO;

animation.fillMode=kCAFillModeForwards;

return animation;

}

+(CABasicAnimation *)moveY:(float)time Y:(NSNumber *)y //纵向移动

{

CABasicAnimation *animation=[CABasicAnimation
animationWithKeyPath:@”transform.translation.y”];

animation.toValue=y;

animation.duration=time;

animation.removedOnCompletion=NO;

animation.fillMode=kCAFillModeForwards;

return animation;

}

+(CABasicAnimation *)scale:(NSNumber *)Multiple orgin:(NSNumber
*)orginMultiple durTimes:(float)time Rep:(float)repeatTimes //缩放

{

CABasicAnimation *animation=[CABasicAnimation
animationWithKeyPath:@”transform.scale”];

animation.fromValue=orginMultiple;

animation.toValue=Multiple;

animation.duration=time;

animation.autoreverses=YES;

animation.repeatCount=repeatTimes;

animation.removedOnCompletion=NO;

animation.fillMode=kCAFillModeForwards;

return animation;

}

+(CAAnimationGroup *)groupAnimation:(NSArray *)animationAry
durTimes:(float)time Rep:(float)repeatTimes //组合动画

{

CAAnimationGroup *animation=[CAAnimationGroup animation];

animation.animations=animationAry;

animation.duration=time;

animation.repeatCount=repeatTimes;

animation.removedOnCompletion=NO;

animation.fillMode=kCAFillModeForwards;

return animation;

}

+(CAKeyframeAnimation *)keyframeAniamtion:(CGMutablePathRef)path
durTimes:(float)time Rep:(float)repeatTimes //路径动画

{

CAKeyframeAnimation *animation=[CAKeyframeAnimation
animationWithKeyPath:@”position”];

animation.path=path;

animation.removedOnCompletion=NO;

animation.fillMode=kCAFillModeForwards;

animation.timingFunction=[CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseIn];

animation.autoreverses=NO;

animation.duration=time;

animation.repeatCount=repeatTimes;

return animation;

}

CAKeyFrameAnimation 关键帧动画 values属性指明整个动画进程中的关键帧点

path和values成效一样 钦定整个动画经过的路径
当values和path同时钦命时,values属性会被忽视。

keyTimes
钦赐三个数组用来为每贰个门道琼斯股票价格平均指数定动画时间要是没有安装keyTimes,系统暗中同意每二个路子的时光为:time
= duration/(values.count –
1),每贰个途径的年华相。如果设置路径的卡通片时间分化时得以流传一个数组
数组的全进程为0和1 如 animation.keyTimes = @[[NSNumber
numberWithFloat:0.0],[NSNumber numberWithFloat:0.2],[NSNumber
numberWithFloat:0.4],[NSNumber numberWithFloat:0.8],[NSNumber
numberWithFloat:1.0]];那么首先段动画时间长度为(0.2-0.0)*
duration,第贰段为(0.4-0.2)*duration 依次总计动画时长。

timeFunctions用以内定时间函数,类似与活动的加快度

kCAMediaTimingFunctionLinear//线性

 kCAMediaTimingFunctionEaseIn//淡入

 kCAMediaTimingFunctionEaseOut//淡出

 kCAMediaTimingFunctionEaseInEaseOut//淡入淡出

 kCAMediaTimingFunctionDefault//默认

calculationMode来决定各样子途径运动的花色

kCAAnimationLinear//默认,线性

 kCAAnimationDiscrete//离散,无中间进程,但keyTimes设置的年华还是生效,物体跳跃地冒出在挨家挨户关键帧上

 kCAAnimationPaced//平均,keyTimes跟timeFunctions失效

 kCAAnimationCubic//平均,同上

 kCAAnimationCubicPaced//平均,同上

图片 4

图片 5

动画组

随之上边的卡通效果,笔者想要在潜移默化的底子上加码叁个形变动画,那么笔者急需创立七个CABasicAnimation对象来形成这一干活:

@IBAction func actionToAnimatedLayer(sender: AnyObject) {
    let opacity = CABasicAnimation(keyPath: "opacity")
    opacity.fromValue = NSNumber(double: 1)
    opacity.toValue = NSNumber(double: 0)
    opacity.duration = 1

    layer.addAnimation(opacity, forKey: "opacity")

    let scale = CABasicAnimation(keyPath: "transform")
    scale.fromValue = NSValue(CATransform3D: CATransform3DIdentity)
    scale.toValue = NSValue(CATransform3D: CATransform3DMakeScale(2, 2, 2))
    scale.duration = 1

    layer.addAnimation(scale, forKey: "scale")
}

除外上边那段代码之外,在CoreAnimation框架中提供了叁个CAAnimationGroup类来将多少个卡通对象整合成一个指标添加到图层上。从使用达成的角度而言,并不会跟下面的代码有别的出入,却能够让代码的逻辑更是鲜明:

@IBAction func actionToAnimatedLayer(sender: AnyObject) {
    // create animations

    let group = CAAnimationGroup()
    group.animations = [opacity, scale]
    group.duration = 1
    layer.addAnimation(group, forKey: "group")
}

按钮动画

第3是卡通片中的形变和透亮渐变分别对应transform以及opacity两个keyPath,其次,动画图层不是按钮本身的图层,因而还索要添加额外的贰个图层。其它,动画存在外扩和内扩的动画效果,由此我们还亟需定义一个枚举来区分:

enum LXDAnimationType {
    case Inner
    case Outer
}

在swift的extension中不协理添加储值属性,由此大家需求运用到runtime的动态绑定来完毕对按钮包蕴动画类型、动画颜色两本性子的恢宏:

private var kAnimationTypeKey: UInt = 0
private var kAnimationColorKey: UInt = 1
extension UIButton {

    enum LXDAnimationType {
        case Inner
        case Outer
    }

    //MARK: - Expand property
    var animationType: LXDAnimationType? {
        get {
            if let type = (objc_getAssociatedObject(self, &kAnimationTypeKey) as? String) {
                return LXDAnimationType(rawValue: type)
            }
            return nil
        }
        set {
            guard newValue != nil else { return }
            self.clipsToBounds = (newValue == .Inner)
            objc_setAssociatedObject(self, &kAnimationTypeKey, newValue!.rawValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    var animationColor: UIColor {
        get {
            if let color = objc_getAssociatedObject(self, &kAnimationColorKey) {
                return color as! UIColor
            }
            return UIColor.whiteColor()
        }
        set {
            objc_setAssociatedObject(self, &kAnimationColorKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
}

接下去是什么确认保证我们在点击按钮的时候能够实行我们的动画片。那里我们通过重写按钮的sendAction(action:to:forEvent:)艺术来推行动画,那几个方法在历次按钮发送三个事变时会被调用。同理,当用户点击按钮时也会调用那么些办法:

//MARK: - Override
public override func sendAction(action: Selector, to target: AnyObject?, forEvent event: UIEvent?) {
    super.sendAction(action, to: target, forEvent: event)

    if let type = animationType {
        var rect: CGRect?
        var radius = self.layer.cornerRadius

        var pos = touchPoint(event)
        let smallerSize = min(self.frame.width, self.frame.height)
        let longgerSize = max(self.frame.width, self.frame.height)
        var scale = longgerSize / smallerSize + 0.5

        switch type {
        case .Inner:
            radius = smallerSize / 2
            rect = CGRect(x: 0, y: 0, width: radius*2, height: radius*2)
            break

        case .Outer:
            scale = 2.5
            pos = CGPoint(x: self.bounds.width/2, y: self.bounds.height/2)
            rect = CGRect(x: pos.x - self.bounds.width, y: pos.y - self.bounds.height, width: self.bounds.width, height: self.bounds.height)
            break
        }

        let layer = animateLayer(rect!, radius: radius, position: pos)
        let group = animateGroup(scale)
        self.layer.addSublayer(layer)
        group.setValue(layer, forKey: "animatedLayer")
        layer.addAnimation(group, forKey: "buttonAnimation")
    }
}

public override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
    if let layer = anim.valueForKey("animatedLayer") as? CALayer {
        layer .removeFromSuperlayer()
    }
}


//MARK: - Private
private func touchPoint(event: UIEvent?) -> CGPoint {
    if let touch = event?.allTouches()?.first {
        return touch.locationInView(self)
    } else {
        return CGPoint(x: self.frame.width/2, y: self.frame.height/2)
    }
}

private func animateLayer(rect: CGRect, radius: CGFloat, position: CGPoint) -> CALayer {
    let layer = CAShapeLayer()
    layer.lineWidth = 1
    layer.position = position
    layer.path = UIBezierPath(roundedRect: rect, cornerRadius: radius).CGPath

    switch animationType! {
    case .Inner:
        layer.fillColor = animationColor.CGColor
        layer.bounds = CGRect(x: 0, y: 0, width: radius*2, height: radius*2)
        break

    case .Outer:
        layer.strokeColor = animationColor.CGColor
        layer.fillColor = UIColor.clearColor().CGColor
        break
    }
    return layer
}

private func animateGroup(scale: CGFloat) -> CAAnimationGroup {
    let opacityAnim = CABasicAnimation(keyPath: "opacity")
    opacityAnim.fromValue = NSNumber(double: 1)
    opacityAnim.toValue = NSNumber(double: 0)

    let scaleAnim = CABasicAnimation(keyPath: "transform")
    scaleAnim.fromValue = NSValue(CATransform3D: CATransform3DIdentity)
    scaleAnim.toValue = NSValue(CATransform3D: CATransform3DMakeScale(scale, scale, scale))

    let group = CAAnimationGroup()
    group.animations = [opacityAnim, scaleAnim]
    group.duration = 0.5
    group.delegate = self
    group.fillMode = kCAFillModeBoth
    group.removedOnCompletion = false
    return group
}

扩充之后的按钮只要设置animationType本条性格之后就会完成在点击时的卡通效果

animateButton.animationType = .Outer

尾话

比较起国外的行使,国内的卡通片效果要显得内敛得多,甚至很多的app是没考虑过动画制作的。然则在移动端支出已然是一片血海的今日,雅观的动画效果依然会为您的选取带来留存,前提是你的施用要可信——单纯的动效留不住人。由此,精晓动画是根本的一项基本技能。本文demo

上一篇:认识CoreAnimation
下一篇:定时器动画

转发请注解本文笔者和转发地址