duck1970 duck1970 - 5 months ago 40
Swift Question

CAShapelayer Duplicates

I'm trying to add a CAShapelayer once every 20ms to given x and y coordinates. I would like the shape to fade away over a second (like a tracer). The function I have created works, the shape is created in the correct location and fades away. But I am getting extra shapes left behind cluttering up the screen.

func shadowBall (x: CGFloat, y: CGFloat){

let xpos : CGFloat = ((self.frame.width/2) + x)
let ypos : CGFloat = ((self.frame.height/2) + y)

let shadowBall = CAShapeLayer()
let shadowBalllRadius :CGFloat = 4
let shadowBallPath : UIBezierPath = UIBezierPath(ovalInRect: CGRect(x: xpos, y: ypos, width: CGFloat(shadowBalllRadius*2), height: CGFloat(shadowBalllRadius*2)))

shadowBall.path = shadowBallPath.CGPath
shadowBall.fillColor = UIColor.clearColor().CGColor
shadowBall.strokeColor = UIColor.whiteColor().CGColor
shadowBall.lineWidth = 0.5

let animation: CABasicAnimation = CABasicAnimation(keyPath: "strokeColor")
animation.fromValue = UIColor.whiteColor().CGColor
animation.toValue = UIColor.clearColor().CGColor
animation.duration = 1.0;
animation.repeatCount = 0;
animation.removedOnCompletion = true
animation.additive = false

self.layer.addSublayer(shadowBall)
shadowBall.addAnimation(animation, forKey: "strokeColor")

}

Rob Rob
Answer

The problem is that when the animation finishes, it restores the strokeColor to the original color. You should really set the strokeColor of the original shape layer to be clearColor(), that way, when you finish animating from whiteColor() to clearColor(), it will remain at clearColor().


Also, I might suggest that you also remove the layer once it's done with the animation so it doesn't continue to consume memory although it's no longer visible.

func shadowBall (x: CGFloat, y: CGFloat) {
    let xpos: CGFloat = ((self.frame.width/2) + x)
    let ypos: CGFloat = ((self.frame.height/2) + y)

    let shadowBall = CAShapeLayer()
    let shadowBalllRadius: CGFloat = 4
    let shadowBallPath = UIBezierPath(ovalInRect: CGRect(x: xpos, y: ypos, width: CGFloat(shadowBalllRadius*2), height: CGFloat(shadowBalllRadius*2)))

    shadowBall.path = shadowBallPath.CGPath
    shadowBall.fillColor = UIColor.clearColor().CGColor
    shadowBall.strokeColor = UIColor.clearColor().CGColor
    shadowBall.lineWidth = 0.1

    let animation = CABasicAnimation(keyPath: "strokeColor")
    animation.fromValue = UIColor.whiteColor().CGColor
    animation.toValue = UIColor.clearColor().CGColor
    animation.duration = 1.0
    animation.repeatCount = 0
    animation.removedOnCompletion = true
    animation.additive = false

    animation.delegate = self
    animation.setValue(shadowBall, forKey: "animationLayer")

    self.layer.addSublayer(shadowBall)
    shadowBall.addAnimation(animation, forKey: "strokeColor")
}

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
    if let layer = anim.valueForKey("animationLayer") {
        layer.removeFromSuperlayer()
    }
}

See How to remove a CALayer-object from animationDidStop?

Comments