Search code examples
iosswift3uiviewanimationcabasicanimation

Fade in/out animation for a series of dots


I have an upload UIButton and I'm trying to implement an animation where I have a group of individual dots that line up horizontally underneath the button and have the very left dot fade in/out, then have the dot next to it fade in/out, and so on all the way to the right most dot, then start the cycle over again from the left. I can get this working for one dot, but what would be the most efficient way to do this for multiple dots?

func dotAnimation(){
    let xCoord = self.recButt.frame.origin.x + 25
    let yCoord = self.recButt.frame.origin.y + 5
    let radius = 3

    let dotPath = UIBezierPath(ovalIn: CGRect(x: Int(xCoord), y: Int(yCoord), width: radius, height: radius))
    let layer = CAShapeLayer()
    layer.path = dotPath.cgPath
    layer.strokeColor = UIColor(red: 95.00/255, green: 106.00/255, blue: 255/255, alpha: 1.00).cgColor
    self.view.layer.addSublayer(layer)

    let animation : CABasicAnimation = CABasicAnimation(keyPath: "opacity");
    animation.autoreverses = true
    animation.fromValue = 0
    animation.toValue = 1
    animation.duration = 2.0
    layer.add(animation, forKey: nil)
}

Solution

  • You are describing a CAReplicatorLayer. Here is one that does the sort of thing you're after, using bars instead of dots:

    enter image description here

    Here's the code for that. Note that there is only one red bar layer; the replication into five, and the offset fading animation, is handled by the containing replicator layer:

        let lay = CAReplicatorLayer()
        lay.frame = CGRect(0,0,100,20)
        let bar = CALayer()
        bar.frame = CGRect(0,0,10,20)
        bar.backgroundColor = UIColor.red.cgColor
        lay.addSublayer(bar)
        lay.instanceCount = 5
        lay.instanceTransform = CATransform3DMakeTranslation(20, 0, 0)
        let anim = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
        anim.fromValue = 1.0
        anim.toValue = 0.2
        anim.duration = 1
        anim.repeatCount = .infinity
        bar.add(anim, forKey: nil)
        lay.instanceDelay = anim.duration / Double(lay.instanceCount)
        // and now just add `lay` to the interface