Swift:CALayer+Extension

JKSwiftExtension,测试用例在 CALayerExtensionViewController 里面
目录:
1、自定义链式编程
2、有关 CABasicAnimation 动画的扩展
3、有关 CAKeyframeAnimation 动画的扩展
4、有关 CATransition 动画的扩展
5、有关 CASpringAnimation 弹簧动画的扩展
6、有关 CAAnimationGroup 动画组的扩展

一、自定义链式编程
// MARK:- 一、自定义链式编程
public extension CALayer {

    // MARK: 1.1、设置圆角
    /// 设置圆角
    /// - Parameter cornerRadius: 圆角
    /// - Returns: 返回自身
    @discardableResult
    func corner(_ cornerRadius: CGFloat) -> Self {
        self.cornerRadius = cornerRadius
        masksToBounds = true
        return self
    }

    // MARK: 1.2、设置背景色
    /// 设置背景色
    /// - Parameter color: 背景色
    /// - Returns: 返回自身
    @discardableResult
    func backgroundColor(_ color: UIColor) -> Self {
        backgroundColor = color.cgColor
        return self
    }

    // MARK: 1.3、设置背景色 (十六进制字符串)
    /// 设置背景色 (十六进制字符串)
    /// - Parameter hex: 背景色 (十六进制字符串)
    /// - Returns: 返回自身
    @discardableResult
    func backgroundColor(_ hex: String) -> Self {
        backgroundColor = UIColor.hexStringColor(hexString: hex).cgColor
        return self
    }

    // MARK: 1.4、设置frame
    /// 设置frame
    /// - Parameter frame: frame
    /// - Returns: 返回自身
    @discardableResult
    func frame(_ frame: CGRect) -> Self {
        self.frame = frame
        return self
    }

    // MARK: 1.5、添加到父视图(UIView)
    /// 添加到父视图
    /// - Parameter superView: 父视图(UIView)
    /// - Returns: 返回自身
    @discardableResult
    func addTo(_ superView: UIView) -> Self {
        superView.layer.addSublayer(self)
        return self
    }

    // MARK: 1.6、添加到父视图(CALayer)
    /// 添加到父视图(CALayer)
    /// - Parameter superLayer: 父视图(CALayer)
    /// - Returns: 返回自身
    @discardableResult
    func addTo(_ superLayer: CALayer) -> Self {
        superLayer.addSublayer(self)
        return self
    }

    // MARK: 1.7、是否隐藏
    /// 是否隐藏
    /// - Parameter isHidden: 是否隐藏
    /// - Returns: 返回自身
    @discardableResult
    func isHidden(_ isHidden: Bool) -> Self {
        self.isHidden = isHidden
        return self
    }

    // MARK: 1.8、设置边框宽度
    /// 设置边框宽度
    /// - Parameter width: 边框宽度
    /// - Returns: 返回自身
    @discardableResult
    func borderWidth(_ width: CGFloat) -> Self {
        self.borderWidth = width
        return self
    }

    // MARK: 1.9、设置边框颜色
    /// 设置边框颜色
    /// - Parameter color: 边框颜色
    /// - Returns: 返回自身
    @discardableResult
    func borderColor(_ color: UIColor) -> Self {
        self.borderColor = color.cgColor
        return self
    }

    // MARK: 1.10、是否开启光栅化
    /// 是否开启光栅化
    /// - Parameter rasterize: 是否开启光栅化
    /// - Returns: 返回自身
    @discardableResult
    func shouldRasterize(_ rasterize: Bool) -> Self {
        self.shouldRasterize = rasterize
        return self
    }

    // MARK: 1.11、设置光栅化比例
    /// 设置光栅化比例
    /// - Parameter scale: 光栅化比例
    /// - Returns: 返回自身
    @discardableResult
    func rasterizationScale(_ scale: CGFloat) -> Self {
        self.rasterizationScale = scale
        self.shouldRasterize = true
        return self
    }

    // MARK: 1.12、设置阴影颜色
    /// 设置阴影颜色
    /// - Parameter color: 阴影颜色
    /// - Returns: 返回自身
    @discardableResult
    func shadowColor(_ color: UIColor) -> Self {
        self.shadowColor = color.cgColor
        return self
    }

    // MARK: 1.13、设置阴影的透明度,取值范围:0~1
    /// 设置阴影的透明度,取值范围:0~1
    /// - Parameter opacity: 阴影的透明度
    /// - Returns: 返回自身
    @discardableResult
    func shadowOpacity(_ opacity: Float) -> Self {
        self.shadowOpacity = opacity
        return self
    }

    // MARK: 1.14、设置阴影的偏移量
    /// 设置阴影的偏移量
    /// - Parameter offset: 偏移量
    /// - Returns: 返回自身
    @discardableResult
    func shadowOffset(_ offset: CGSize) -> Self {
        self.shadowOffset = offset
        return self
    }

    // MARK: 1.15、设置阴影圆角
    /// 设置阴影圆角
    /// - Parameter radius: 圆角大小
    /// - Returns: 返回自身
    @discardableResult
    func shadowRadius(_ radius: CGFloat) -> Self {
        self.shadowRadius = radius
        return self
    }

    // MARK: 1.16、高性能添加阴影路径 Shadow Path,提示:当用bounds设置path时,看起来的效果与只设置了shadowOpacity一样,但是添加了shadowPath后消除了离屏渲染问题
    /// 高性能添加阴影 Shadow Path
    /// - Parameter path: 阴影Path
    /// - Returns: 返回自身
    @discardableResult
    func shadowPath(_ path: CGPath) -> Self {
        self.shadowPath = path
        return self
    }
}
二、有关 CABasicAnimation 动画的扩展
// MARK:- 二、有关 CABasicAnimation 动画的扩展
public extension CALayer {

    // MARK: 2.1、移动到另外一个 点(point)
    /// 从一个 点(point) 移动到另外一个 点(poi
    /// - Parameters:
    ///   - endPoint: 终点
    ///   - duration: 持续时间
    ///   - delay: 几秒后执行
    ///   - repeatNumber: 重复次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func animationMovePoint(to endPoint: CGPoint,
                            duration: TimeInterval,
                            delay: TimeInterval = 0,
                            repeatNumber: Float = 1,
                            removedOnCompletion: Bool = false,
                            option: CAMediaTimingFunctionName = .default) {
        baseBasicAnimation(keyPath: "position", startValue: position, endValue: endPoint, duration: duration, delay: delay, repeatNumber: repeatNumber, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 2.2、移动X
    /// 移动X
    /// - Parameters:
    ///   - moveValue: 移动到的X值
    ///   - duration:  动画持续的时间
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func animationMoveX(moveValue: Any?,
                        duration: TimeInterval = 2.0,
                        delay: TimeInterval = 0,
                        repeatNumber: Float = 1,
                        removedOnCompletion: Bool = false,
                        option: CAMediaTimingFunctionName = .default) {
        baseBasicAnimation(keyPath: "transform.translation.x", startValue: position, endValue: moveValue, duration: duration, delay: delay, repeatNumber: repeatNumber, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 2.3、移动Y
    /// 移动Y
    /// - Parameters:
    ///   - moveValue: 移动到的Y值
    ///   - duration:  动画持续的时间
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func animationMoveY(moveValue: Any?,
                        duration: TimeInterval = 2.0,
                        delay: TimeInterval = 0,
                        repeatNumber: Float = 1,
                        removedOnCompletion: Bool = false,
                        option: CAMediaTimingFunctionName = .default) {
        baseBasicAnimation(keyPath: "transform.translation.y", startValue: position, endValue: moveValue, duration: duration, delay: delay, repeatNumber: repeatNumber, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 2.4、圆角动画
    /// 圆角动画
    /// - Parameters:
    ///   - cornerRadius: 圆角大小
    ///   - duration:  动画持续的时间
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func animationCornerRadius(cornerRadius: Any?,
                               duration: TimeInterval = 2.0,
                               delay: TimeInterval = 0,
                               repeatNumber: Float = 1,
                               removedOnCompletion: Bool = false,
                               option: CAMediaTimingFunctionName = .default) {
        baseBasicAnimation(keyPath: "cornerRadius", startValue: position, endValue: cornerRadius, duration: duration, delay: delay, repeatNumber: repeatNumber, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 2.5、缩放动画
    /// 缩放动画
    /// - Parameters:
    ///   - scaleValue: 放大的倍数
    ///   - duration:  动画持续的时间
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func animationScale(scaleValue: Any?,
                        duration: TimeInterval = 2.0,
                        delay: TimeInterval = 0,
                        repeatNumber: Float = 1,
                        removedOnCompletion: Bool = true,
                        option: CAMediaTimingFunctionName = .default) {
        baseBasicAnimation(keyPath: "transform.scale", startValue: 1, endValue: scaleValue, duration: duration, delay: delay, repeatNumber: repeatNumber, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 2.6、旋转动画
    /// 缩放动画
    /// - Parameters:
    ///   - rotation: 旋转的角度
    ///   - duration:  动画持续的时间
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func animationRotation(rotation: Any?,
                           duration: TimeInterval = 2.0,
                           delay: TimeInterval = 0,
                           repeatNumber: Float = 1,
                           removedOnCompletion: Bool = true,
                           option: CAMediaTimingFunctionName = .default) {
        baseBasicAnimation(keyPath: "transform.rotation", startValue: nil, endValue: rotation, duration: duration, delay: delay, repeatNumber: repeatNumber, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: CABasicAnimation 动画的基础方法
    /// 动画的 X/Y 移动
    /// - Parameters:
    ///   - keyPath: 动画的类型
    ///   - moveValue: 移动到的位置
    ///   - duration:  动画持续的时间
    ///   - repeatNumber: 重复的次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func baseBasicAnimation(keyPath: String,
                            startValue: Any?,
                            endValue: Any?,
                            duration: TimeInterval = 2.0,
                            delay: TimeInterval = 0,
                            repeatNumber: Float = 1,
                            removedOnCompletion: Bool = false,
                            option: CAMediaTimingFunctionName = .default) {
        let translatonAnimation: CABasicAnimation = CABasicAnimation()
        // 几秒后执行
        translatonAnimation.beginTime = CACurrentMediaTime() + delay
        // 动画的类型
        translatonAnimation.keyPath = keyPath
        // 起始的值
        if let weakStartValue = startValue {
            translatonAnimation.fromValue = weakStartValue
        }
        // 结束的值
        translatonAnimation.toValue = endValue
        // 动画持续的时间
        translatonAnimation.duration = duration
        // 运动后的位置保持不变(layer的最后位置是toValue)
        translatonAnimation.isRemovedOnCompletion = removedOnCompletion
        // 图层保持动画执行后的状态,前提是fillMode设置为kCAFillModeForwards
        translatonAnimation.fillMode = CAMediaTimingFillMode.forwards
        /**
         linear: 匀速
         easeIn: 慢进
         easeOut: 慢出
         easeInEaseOut: 慢进慢出
         default: 慢进慢出
         */
        translatonAnimation.timingFunction = CAMediaTimingFunction(name: option)
        add(translatonAnimation, forKey: nil)
    }
}
三、有关 CAKeyframeAnimation 动画的扩展
// MARK:- 三、有关 CAKeyframeAnimation 动画的扩展
public extension CALayer {

    // MARK: 3.1、设置values使其沿 position 运动 (这里移动是以 视图的 锚点移动的,默认是视图的 中心点)
    /// 设置values使其沿 position 运动 (这里移动是以 视图的 锚点移动的,默认是视图的 中心点)
    /// - Parameters:
    ///   - values: CGPoint 点
    ///   - keyTimes: 设置关键帧对应的时间点,范围:0-1。如果没有设置该属性,则每一帧的时间平分。
    ///   - duration: 动画持续的时间
    ///   - repeatNumber: 重复的次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func addKeyframeAnimationPosition(values: [Any],
                                      keyTimes: [NSNumber]?,
                                      duration: TimeInterval = 2.0,
                                      delay: TimeInterval = 0,
                                      repeatNumber: Float = 1,
                                      removedOnCompletion: Bool = false,
                                      option: CAMediaTimingFunctionName = .default) {
        baseKeyframeAnimation(keyPath: "position", values: values, keyTimes: keyTimes, duration: duration, delay: delay, repeatNumber: repeatNumber, path: nil, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 3.2、设置 角度值 抖动
    /// 设置 角度值 抖动
    /// - Parameters:
    ///   - values: CGPoint 点
    ///   - keyTimes: 设置关键帧对应的时间点,范围:0-1。如果没有设置该属性,则每一帧的时间平分。
    ///   - duration: 动画持续的时间
    ///   - repeatNumber: 重复的次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func addKeyframeAnimationRotation(values: [Any] = [CGFloat(-5).degreesToRadians(), CGFloat(5).degreesToRadians(), CGFloat(-5).degreesToRadians()],
                                      keyTimes: [NSNumber]?,
                                      duration: TimeInterval = 1.0,
                                      delay: TimeInterval = 0,
                                      repeatNumber: Float = 1,
                                      removedOnCompletion: Bool = true,
                                      option: CAMediaTimingFunctionName = .default) {
        baseKeyframeAnimation(keyPath: "transform.rotation", values: values, keyTimes: keyTimes, duration: duration, delay: delay, repeatNumber: repeatNumber, path: nil, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 3.3、根据 CGPath 进行做 移动 动画
    /// 根据 CGPath 进行做 移动 动画
    /// - Parameters:
    ///   - path: CGPath 路径
    ///   - duration: 动画时长
    ///   - repeatNumber: 重复次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func addKeyframeAnimationPositionBezierPath(path: CGPath?,
                                                duration: TimeInterval = 2.0,
                                                delay: TimeInterval = 0,
                                                repeatNumber: Float = 1,
                                                removedOnCompletion: Bool = false,
                                                option: CAMediaTimingFunctionName = .default) {
        baseKeyframeAnimation(keyPath: "position", values: nil, keyTimes: nil, duration: duration,  delay: delay, repeatNumber: repeatNumber, path: path, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: CAKeyframeAnimation 动画的通用方法
    /// CAKeyframeAnimation 动画的通用方法
    /// - Parameters:
    ///   - keyPath: 动画路径对象,可以指定一个路径,在执行动画时路径会沿着路径移动,Path在动画中只会影响视图的Position。
    ///   - values: 关键帧数组对象,里面每一个元素即为一个关键帧,动画会在对应的时间段内,依次执行数组中每一个关键帧的动画
    ///   - keyTimes: 设置关键帧对应的时间点,范围:0-1。如果没有设置该属性,则每一帧的时间平分。
    ///   - duration: 动画持续的时间
    ///   - repeatNumber: 重复的次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func baseKeyframeAnimation(keyPath: String,
                               values: [Any]?,
                               keyTimes: [NSNumber]?,
                               duration: TimeInterval = 2.0,
                               delay: TimeInterval = 0,
                               repeatNumber: Float = 1,
                               path: CGPath?,
                               removedOnCompletion: Bool = false,
                               option: CAMediaTimingFunctionName = .default) {
        let keyframeAnimation = CAKeyframeAnimation(keyPath: keyPath)
        keyframeAnimation.duration = duration
        // 几秒后执行
        keyframeAnimation.beginTime = CACurrentMediaTime() + delay
        keyframeAnimation.isRemovedOnCompletion = removedOnCompletion
        keyframeAnimation.fillMode = .forwards
        keyframeAnimation.repeatCount = repeatNumber
        // 传进来的值,可以是一组 CGPoint
        if let weakValues = values {
            keyframeAnimation.values = weakValues
        }
        if let weakKeyTimes = keyTimes {
            keyframeAnimation.keyTimes = weakKeyTimes
        }
        if let weakPath = path {
            keyframeAnimation.path = weakPath
            // 计算模式 -> 强制运动,匀速进行,不管路径有多远!否则一次动画结束会有短暂停顿
            keyframeAnimation.calculationMode = .cubicPaced
        }
        // 旋转模式 -> 沿着路径,自行旋转 转的时候需要沿着路径的切线!进行转动!
        // keyframeAnimation.rotationMode = .rotateAuto
        //  动画的时间节奏控制 方式
        keyframeAnimation.timingFunction = CAMediaTimingFunction(name: option)
        add(keyframeAnimation, forKey: nil)
    }
}
四、有关 CATransition 动画的扩展
// MARK:- 四、有关 CATransition 动画的扩展
/**
 转场动画,比UIVIew转场动画具有更多的动画效果,比如Nav的默认Push视图的效果就是通过CATransition的kCATransitionPush类型来实现。
 */
public extension CALayer {

    // MARK: 4.1、转场动画的使用
    /// 转场动画的使用
    /// - Parameters:
    ///   - type: 过渡动画的类型:
    ///   - subtype: 过渡动画的方向
    ///   - duration: 动画的时间
    func addTransition(type: CATransitionType,
                       subtype: CATransitionSubtype?,
                       duration: CFTimeInterval = 2.0,
                       delay: TimeInterval = 0) {
        let transition = CATransition()
        // 几秒后执行
        transition.beginTime = CACurrentMediaTime() + delay
        /**
         过渡动画的类型
         fade: 渐变
         moveIn: 覆盖
         push: 推出
         reveal: 揭开
         还有一些私有动画类型,效果很炫酷,不过不推荐使用。
       私有动画类型的值有:"cube"、"suckEffect"、"oglFlip"、 "rippleEffect"、"pageCurl"、"pageUnCurl"等等
         */
        transition.type = type
        /**
         fromRight: 从右边
         fromLeft:  从左边
         fromTop: 从顶部
         fromBottom: 从底部
         */
        transition.subtype = .fromLeft
        transition.duration = 1
        add(transition, forKey: nil)
    }
}    
五、有关 CASpringAnimation 弹簧动画的扩展
// MARK:- 五、有关 CASpringAnimation 弹簧动画的扩展
/**
 CASpringAnimation是 iOS9 新加入动画类型,是CABasicAnimation的子类,用于实现弹簧动画
 CASpringAnimation和UIView的SpringAnimation对比:
 1.CASpringAnimation 可以设置更多影响弹簧动画效果的属性,可以实现更复杂的弹簧动画效果,且可以和其他动画组合。
 2.UIView的SpringAnimation实际上就是通过CASpringAnimation来实现。
 */
public extension CALayer {

    // MARK: 5.1、弹簧动画:Bounds 动画
    /// 弹簧动画
    /// - Parameters:
    ///   - mass: 质量(影响弹簧的惯性,质量越大,弹簧惯性越大,运动的幅度越大)
    ///   - stiffness: 弹性系数(弹性系数越大,弹簧的运动越快)
    ///   - damping: 阻尼系数(阻尼系数越大,弹簧的停止越快)
    ///   - initialVelocity: 初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)
    ///   - repeatNumber: 动画执行的次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func addSpringAnimationBounds(toValue: Any?,
                                  delay: TimeInterval = 0,
                                  mass: CGFloat = 10.0,
                                  stiffness: CGFloat = 5000,
                                  damping: CGFloat = 100.0,
                                  initialVelocity: CGFloat = 5,
                                  repeatNumber: Float = 1,
                                  removedOnCompletion: Bool = false,
                                  option: CAMediaTimingFunctionName = .default) {
        baseSpringAnimation(path: "bounds", toValue: toValue, mass: mass, stiffness: stiffness, damping: damping, initialVelocity: initialVelocity, repeatNumber: repeatNumber, removedOnCompletion: removedOnCompletion, option: option)
    }

    // MARK: 弹簧的基类动画
    ///  弹簧的基类动画
    /// - Parameters:
    ///   - path: 动画路径对象,可以指定一个路径,在执行动画时路径会沿着路径移动
    ///   - mass: 质量(影响弹簧的惯性,质量越大,弹簧惯性越大,运动的幅度越大)
    ///   - stiffness: 弹性系数(弹性系数越大,弹簧的运动越快)
    ///   - damping: 阻尼系数(阻尼系数越大,弹簧的停止越快)
    ///   - initialVelocity: 初始速率(弹簧动画的初始速度大小,弹簧运动的初始方向与初始速率的正负一致,若初始速率为0,表示忽略该属性)
    ///   - repeatNumber: 动画执行的次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func baseSpringAnimation(path: String?,
                             toValue: Any?,
                             delay: TimeInterval = 0,
                             mass: CGFloat = 10.0,
                             stiffness: CGFloat = 5000,
                             damping: CGFloat = 100.0,
                             initialVelocity: CGFloat = 5,
                             repeatNumber: Float = 1,
                             removedOnCompletion: Bool = false,
                             option: CAMediaTimingFunctionName = .default) {
        let springAnimation = CASpringAnimation(keyPath: path)
        // 几秒后执行
        springAnimation.beginTime = CACurrentMediaTime() + delay
        // 质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
        springAnimation.mass = mass
        // 刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
        springAnimation.stiffness = stiffness
        // 阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,停止越快
        springAnimation.damping = damping
        // 初始速率,动画视图的初始速度大小;速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
        springAnimation.initialVelocity = initialVelocity
        // settlingDuration:结算时间(根据动画参数估算弹簧开始运动到停止的时间,动画设置的时间最好根据此时间来设置)
        springAnimation.duration = springAnimation.settlingDuration
        springAnimation.toValue = toValue
        springAnimation.isRemovedOnCompletion = removedOnCompletion
        springAnimation.fillMode = CAMediaTimingFillMode.forwards
        // 动画的时间节奏控制 方式
        springAnimation.timingFunction = CAMediaTimingFunction(name: option)
        add(springAnimation, forKey: nil)
    }
}
六、有关 CAAnimationGroup 动画组的扩展
// MARK:- 六、有关 CAAnimationGroup 动画组的扩展
/**
 使用Group可以将多个动画合并一起加入到层中,Group中所有动画并发执行,可以方便地实现需要多种类型动画的场景
 */
public extension CALayer {

    // MARK: CAAnimationGroup 的基类动画
    /// CAAnimationGroup 的基类动画
    /// - Parameters:
    ///   - animations: 动画组
    ///   - duration: 动画时长
    ///   - repeatNumber: 重复次数
    ///   - removedOnCompletion: 运动后的位置保持不变(layer的最后位置是toValue)
    ///   - option: 动画的时间节奏控制 方式
    func baseAnimationGroup(animations: [CAAnimation]?,
                            duration: TimeInterval = 2.0,
                            delay: TimeInterval = 0,
                            repeatNumber: Float = 1,
                            removedOnCompletion: Bool = false,
                            option: CAMediaTimingFunctionName = .default) {
        let animationGroup = CAAnimationGroup()
        // 几秒后执行
        animationGroup.beginTime = CACurrentMediaTime() + delay
        animationGroup.animations = animations
        animationGroup.duration = duration
        animationGroup.fillMode = .forwards;
        animationGroup.isRemovedOnCompletion = removedOnCompletion
        // 动画的时间节奏控制 方式
        animationGroup.timingFunction = CAMediaTimingFunction(name: option)
        add(animationGroup, forKey: nil)
    }
}

推荐阅读更多精彩内容