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:
This is my current output
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.
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: