1

I am working on an iOS app where I need to create ripple effect on a UIView. I am able to create the ripple effect but I am not able to customise it. This is what I want to achive:

enter image description here

This is my current output

enter image description here

What I want to achieve is create 4-5 ripples continuously and then after some delay another 4-5 ripple and repeat the process infinitely. But currently I am only able to create 1 ripple (it does go infinitely).

Here is my code:

func addRipple(rView: UIView) {
    let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: rView.bounds.size.width, height: rView.bounds.size.height))

    let shapePosition = CGPoint(x: rView.bounds.size.width / 2.0, y: rView.bounds.size.height / 2.0)
    let rippleShape = CAShapeLayer()
    rippleShape.bounds = CGRect(x: 0, y: 0, width: rView.bounds.size.width, height: rView.bounds.size.height)
    rippleShape.path = path.cgPath
    rippleShape.fillColor = UIColor.clear.cgColor
    rippleShape.strokeColor = UIColor.systemBlue.cgColor
    rippleShape.lineWidth = 1
    rippleShape.position = shapePosition
    rippleShape.opacity = 0
    
    rView.layer.addSublayer(rippleShape)

    let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
    scaleAnim.fromValue = NSValue(caTransform3D: CATransform3DIdentity)
    scaleAnim.toValue = NSValue(caTransform3D: CATransform3DMakeScale(2, 2, 1))

    let opacityAnim = CABasicAnimation(keyPath: "opacity")
    opacityAnim.fromValue = 1
    opacityAnim.toValue = nil

    let animation = CAAnimationGroup()
    animation.animations = [scaleAnim, opacityAnim]
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
    animation.duration = 1
    animation.repeatCount = Float.infinity
    animation.isRemovedOnCompletion = false
    rippleShape.add(animation, forKey: "rippleEffect")
}

This is how I call the addRipple method

@IBOutlet weak var sView: UIView!
override func viewDidLoad() {
    super.viewDidLoad()
    sView.layer.cornerRadius = sView.frame.size.width / 2
    sView.layer.borderWidth = 2
    sView.layer.borderColor = UIColor.systemBlue.cgColor
    addRipple(to: sView)
}

I think I am very close but I am not able to figure it out.

Thanks in advance.

George
  • 3,600
  • 2
  • 26
  • 36

1 Answers1

1

If I understand correctly, you want multiple of these ripples to appear on screen at a time. In that case, it would be more convenient to work with if you created multiple circle sublayers, and animate each of them with a different timeOffset.

let path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: rView.bounds.size.width, height: rView.bounds.size.height))
let shapePosition = CGPoint(x: rView.bounds.size.width / 2.0, y: rView.bounds.size.height / 2.0)

for i in 0..<4 {
    let rippleShape = CAShapeLayer()
    
    // set up rippleShape as usual here...
    
    rView.layer.addSublayer(rippleShape)
    
    // set these up as usual too...
    let scaleAnim = ...
    let opacityAnim = ...

    let animation = CAAnimationGroup()
    animation.animations = [scaleAnim, opacityAnim]
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
    animation.repeatCount = Float.infinity
    animation.duration = 2
    animation.isRemovedOnCompletion = false

    // this is the important section
    let timeInBetweenRipples = 0.2
    animation.timeOffset = timeInBetweenRipples * Double(i)
    animation.speed = 0.5
    // You can tweak speed, timeInBetweenRipples, and duration as you see fit

    // don't forget to give each animation a different key
    rippleShape.add(animation, forKey: "rippleEffect\(i)")
}

Here's how one frame of the animation looks like:

enter image description here

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • This solution is great, it solves the problem of multiple ripples, but I am also trying to add a delay between the 2 frames of ripples.(ie 4 ripples are generated and then wait for 2 seconds and then next set of 4 ripples are generated and so on). – George Apr 24 '23 at 09:39
  • speed, timeInBetweenRipples, and duration tweaking does not help in creating a delay after each frame(frame means set of 4 ripples) of ripples – George Apr 24 '23 at 09:40
  • @George As far as I can tell there is definitely a delay between generating each set of 4 ripples. Do you mean you want to wait for all the ripple animations to finish, *and only then* start waiting 2 seconds? – Sweeper Apr 24 '23 at 09:43
  • yes exactly that is what I am trying to achieve. – George Apr 24 '23 at 09:48
  • 1
    @George I see. Try setting `opacityAnim.duration` and `scaleAnim.duration` to 2 then, and set the animation group's duration to be more than 2. – Sweeper Apr 24 '23 at 09:51