0

I have facing issue to make ripples in Square and Stare figure like YRipple

Please help me and suggestion always welcome.

Furkan
  • 306
  • 4
  • 17
  • 1
    Are you asking how would you make a ripple effect where shapes would be squares and stars instead of circles? So basically creating these shapes and animating them so they increase in size and fade away? – Matic Oblak Mar 16 '22 at 10:26

1 Answers1

0

One easy way to achieve this is to use UIView animations. Each ripple is simply an instance of UIView. The shape can then be simply defined, drawn in one of many ways. I am using the override of draw rect method:

class RippleEffectView: UIView {

    func addRipple(at location: CGPoint) {
        let minRadius: CGFloat = 5.0
        let maxRadius: CGFloat = 100.0
        let startFrame = CGRect(x: location.x - minRadius, y: location.y - minRadius, width: minRadius*2.0, height: minRadius*2.0)
        let endFrame = CGRect(x: location.x - maxRadius, y: location.y - maxRadius, width: maxRadius*2.0, height: maxRadius*2.0)
        let view = ShapeView(frame: startFrame)
        view.shape = .star(cornerCount: 5)
        view.backgroundColor = .clear
        view.contentMode = .redraw
        view.strokeColor = .black
        view.strokeWidth = 5.0
        addSubview(view)
        UIView.animate(withDuration: 1.0, delay: 0.0, options: [.allowUserInteraction]) {
            view.frame = endFrame
            view.alpha = 0.0
        } completion: { _ in
            view.removeFromSuperview()
        }

    }
    
}

private class ShapeView: UIView {
    
    var fillColor: UIColor?
    var strokeColor: UIColor?
    var strokeWidth: CGFloat = 0.0
    
    var shape: Shape = .rectangle
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        let path = generatePath()
        path.lineWidth = strokeWidth
        
        if let fillColor = fillColor {
            fillColor.setFill()
            path.fill()
        }
        if let strokeColor = strokeColor {
            strokeColor.setStroke()
            path.stroke()
        }
        
    }
    
    private func generatePath() -> UIBezierPath {
        switch shape {
        case .rectangle: return UIBezierPath(rect: bounds.insetBy(dx: strokeWidth*0.5, dy: strokeWidth*0.5))
        case .oval: return UIBezierPath(ovalIn: bounds.insetBy(dx: strokeWidth*0.5, dy: strokeWidth*0.5))
        case .anglesOnCircle(let cornerCount):
            guard cornerCount > 2 else { return .init() }
            let center = CGPoint(x: bounds.midX, y: bounds.midY)
            let radius = min(bounds.width, bounds.height)*0.5 - strokeWidth*0.5
            let path = UIBezierPath()
            for index in 0..<cornerCount {
                let angle = CGFloat(index)/CGFloat(cornerCount) * (.pi*2.0)
                let point = CGPoint(x: center.x + cos(angle)*radius,
                                    y: center.y + sin(angle)*radius)
                if index == 0 {
                    path.move(to: point)
                } else {
                    path.addLine(to: point)
                }
            }
            path.close()
            return path
        case .star(let cornerCount):
            guard cornerCount > 2 else { return .init() }
            let center = CGPoint(x: bounds.midX, y: bounds.midY)
            let outerRadius = min(bounds.width, bounds.height)*0.5 - strokeWidth*0.5
            let innerRadius = outerRadius*0.7
            let path = UIBezierPath()
            for index in 0..<cornerCount*2 {
                let angle = CGFloat(index)/CGFloat(cornerCount) * .pi
                let radius = index.isMultiple(of: 2) ? outerRadius : innerRadius
                let point = CGPoint(x: center.x + cos(angle)*radius,
                                    y: center.y + sin(angle)*radius)
                if index == 0 {
                    path.move(to: point)
                } else {
                    path.addLine(to: point)
                }
            }
            path.close()
            return path
        }
    }
    
}

private extension ShapeView {
    
    enum Shape {
        case rectangle
        case oval
        case anglesOnCircle(cornerCount: Int)
        case star(cornerCount: Int)
    }
    
}

I used it in a view controller where I replaced main view with this ripple view in Storyboard.

class ViewController: UIViewController {

    private var rippleView: RippleEffectView? { view as? RippleEffectView }
    
    override func viewDidLoad() {
        super.viewDidLoad()
       
        
        rippleView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(onTap)))
    }
    
    @objc private func onTap(_ recognizer: UIGestureRecognizer) {
        let location = recognizer.location(in: rippleView)
        rippleView?.addRipple(at: location)
    }


}

I hope the code speaks for itself. It should be no problem to change colors. You could apply some rotation by using transform on each ripple view...

You could even use images instead of shapes. If image is set to be as templates you could even change colors using tint property on image view... So limitless possibilities.

Matic Oblak
  • 16,318
  • 3
  • 24
  • 43