使用 CABasicAnimation 正确设置圆半径动画

Properly animating circle radius with CABasicAnimation

我想用CABasicAnimation扩大和缩小一个圆。我之前设计了一种干净有效的方法来使用 sinUIView class 的 drawRect 方法绘制动画,但不幸的是,你似乎无法动画或强制drawRect 触发 UIView.animate 方法,无论您做什么。因此,我不得不想出一个使用 CABasicAnimation 的解决方法。结果有点效果,但动画很丑而且看起来不对。这是我的意思的一个例子:

涨缩时出现翘边,涨圈时右侧出现凹陷或空洞。这是负责动画的class:

struct PathConfig {
    var arcCenter: CGPoint
    var radius: CGFloat
    let startAngle: CGFloat = 0.0
    let endAngle: CGFloat = CGFloat(2 * M_PI)
}

class RGSStandbyIndicatorView: UIView {

    // MARK: - Variables & Constants

    /// The drawing layer.
    var shapeLayer: CAShapeLayer!

    // MARK: - Class Methods

    /// Animation function.
    func animate(period: CFTimeInterval) {
        let p: PathConfig = PathConfig(arcCenter: CGPoint(x: frame.width / 2, y: frame.height / 2) , radius: 17.5)
        let b: UIBezierPath = UIBezierPath(arcCenter: p.arcCenter, radius: p.radius, startAngle: p.startAngle, endAngle: p.endAngle, clockwise: true)
        let a: CABasicAnimation = CABasicAnimation(keyPath: "path")

        a.fromValue = shapeLayer.path
        a.toValue = b.cgPath
        a.autoreverses = true
        a.duration = period
        a.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        a.repeatCount = HUGE
        shapeLayer.add(a, forKey: "animateRadius")
    }

    /// Initializes and returns a CAShapeLayer with the given draw path and fill color.
    private class func shapeLayerForPath(_ path: CGPath, with fillColor: CGColor) -> CAShapeLayer {
        let shapeLayer: CAShapeLayer = CAShapeLayer()
        shapeLayer.path = path
        shapeLayer.fillColor = fillColor
        return shapeLayer
    }

    /// Configures the UIView
    private func setup() {

        // Init config, path.
        let cfg: PathConfig = PathConfig(arcCenter: CGPoint(x: frame.width / 2, y: frame.height / 2) , radius: 0.0)
        let path: UIBezierPath = UIBezierPath(arcCenter: cfg.arcCenter, radius: cfg.radius, startAngle: cfg.startAngle, endAngle: cfg.endAngle, clockwise: true)

        // Init CAShapeLayer
        shapeLayer = RGSStandbyIndicatorView.shapeLayerForPath(path.cgPath, with: UIColor.white.cgColor)

        layer.addSublayer(shapeLayer)
    }

    // MARK: Class Method Overrides

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }

}

我已经尝试调整开始和结束角度,希望它是没有重叠的路径的一些人工制品,但它没有改变任何东西。我不确定如何解决这个问题,它基于查德威克伍德 answer 对类似问题的回答。

使用这个 UIView 子类

class RadioAnimationView: UIView {

    var animatableLayer : CAShapeLayer?

    override func awakeFromNib() {
        super.awakeFromNib()
        self.layer.cornerRadius = self.bounds.height/2

        self.animatableLayer = CAShapeLayer()
        self.animatableLayer?.fillColor = self.backgroundColor?.cgColor
        self.animatableLayer?.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
        self.animatableLayer?.frame = self.bounds
        self.animatableLayer?.cornerRadius = self.bounds.height/2
        self.animatableLayer?.masksToBounds = true
        self.layer.addSublayer(self.animatableLayer!)
        self.startAnimation()
    }


    func startAnimation()
    {
        let layerAnimation = CABasicAnimation(keyPath: "transform.scale")
        layerAnimation.fromValue = 1
        layerAnimation.toValue = 3
        layerAnimation.isAdditive = false
        layerAnimation.duration = CFTimeInterval(2)
        layerAnimation.fillMode = kCAFillModeForwards
        layerAnimation.isRemovedOnCompletion = true
        layerAnimation.repeatCount = .infinity
        layerAnimation.autoreverses = true

        self.animatableLayer?.add(layerAnimation, forKey: "growingAnimation")
    }

}

希望对您有所帮助