I am trying to animate the paths of a CAShapeLayer so that I get the effect of a circle "filling" up to a specific amount.
The Issue
It "works", but is not AS smooth as I think it could be, and I want to introduce some easing to it. But because I'm animating each % individually, I'm not sure that's possible right now. So I'm looking for alternatives that could solve this issue!
Visual Explanation
To start -- here are some "frames" of the animation that I'm trying to achieve (see images - circle fills from 0% to 25%)
Code
I create the green stroke (outside):
let ovalPath = UIBezierPath(ovalInRect: CGRectMake(20, 20, 240, 240))
let circleStrokeLayer = CAShapeLayer()
circleStrokeLayer.path = ovalPath.CGPath
circleStrokeLayer.lineWidth = 20
circleStrokeLayer.fillColor = UIColor.clearColor().CGColor
circleStrokeLayer.strokeColor = colorGreen.CGColor
containerView.layer.addSublayer(circleStrokeLayer)
I create the initial path of the filled shape (inside):
let filledPathStart = UIBezierPath(arcCenter: CGPoint(x: 140, y: 140), radius: 120, startAngle: startAngle, endAngle: Math().percentToRadians(0), clockwise: true)
filledPathStart.addLineToPoint(CGPoint(x: 140, y: 140))
filledLayer = CAShapeLayer()
filledLayer.path = filledPathStart.CGPath
filledLayer.fillColor = colorGreen.CGColor
containerView.layer.addSublayer(filledLayer)
I then animate with a for loop and array of CABasicAnimations.
var animations: [CABasicAnimation] = [CABasicAnimation]()
func animate() {
for val in 0...25 {
let morph = CABasicAnimation(keyPath: "path")
morph.duration = 0.01
let filledPathEnd = UIBezierPath(arcCenter: CGPoint(x: 140, y: 140), radius: 120, startAngle: startAngle, endAngle: Math().percentToRadians(CGFloat(val)), clockwise: true)
filledPathEnd.addLineToPoint(CGPoint(x: 140, y: 140))
morph.delegate = self
morph.toValue = filledPathEnd.CGPath
animations.append(morph)
}
applyNextAnimation()
}
func applyNextAnimation() {
if animations.count == 0 {
return
}
let nextAnimation = animations[0]
animations.removeAtIndex(0)
filledLayer.addAnimation(nextAnimation, forKey: nil)
}
override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
filledLayer.path = (anim as! CABasicAnimation).toValue as! CGPath
applyNextAnimation()
}