Search code examples
iosswiftdrawinguibezierpath

Draw waves between 2 points with UIBezierPath


I'm trying to give a UITableViewCell a 'waved' bottom to make it look like a 'ripped off paper' effect (for an order receipt). I want to draw this over the whole length of the cell.

I found this solution on StackOverflow, which creates one single sine between two points.

I tried altering that code to make the effect I need (I know there's a lot wrong in my code):

    let path = UIBezierPath()
     let origin = CGPoint(x: 0, y: bounds.size.height / 2)
     path.move(to: origin)

      let graphWidth: CGFloat = 0.8  // Graph is 80% of the width of the view
     let amplitude: CGFloat = 0.5   // Amplitude of sine wave is 30% of view

     for angle in stride(from: 1.0, through: bounds.size.width * 5.0, by: 1.0) {
         let x = origin.x + CGFloat(angle/360.0) * bounds.size.width * (360 / (bounds.size.width * 10.0))
         let y = origin.y - CGFloat(sin(angle/180.0 * CGFloat.pi)) * bounds.size.height * amplitude * (360 / (bounds.size.width * 10.0))
         path.addLine(to: CGPoint(x: x, y: y))
     }

What I'm trying to reach is: wave

What would be the best approach here? If I could get above solution working and looking like the image, that would be perfect. If anyone has other suggestions, I'm open to everything.


Solution

  • All credit to vacawama answer. You can achieve this as below,

    class SineView: UIView {
        let graphWidth: CGFloat = 0.15
        let amplitude: CGFloat = 0.1
    
        override func draw(_ rect: CGRect) {
            let width = rect.width
            let height = rect.height
    
            let origin = CGPoint(x: 0, y: height * 0.50)
    
            let path = UIBezierPath()
            path.move(to: origin)
    
            var endY: CGFloat = 0.0
            let step = 5.0
            for angle in stride(from: step, through: Double(width) * (step * step), by: step) {
                let x = origin.x + CGFloat(angle/360.0) * width * graphWidth
                let y = origin.y - CGFloat(sin(angle/180.0 * Double.pi)) * height * amplitude
                path.addLine(to: CGPoint(x: x, y: y))
                endY = y
            }
            path.addLine(to: CGPoint(x: width, y: endY))
            path.addLine(to: CGPoint(x: width, y: height))
            path.addLine(to: CGPoint(x: 0, y: height))
            path.addLine(to: CGPoint(x: 0, y: origin.y))
    
            UIColor.black.setFill()
            path.fill()
            UIColor.black.setStroke()
            path.stroke()
        }
    }
    

    Usage

    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let size = view.frame.size
            let sineView = SineView(frame: CGRect(x: 0, y: 100, width: size.width, height: 60))
            sineView.backgroundColor = .white
            self.view.addSubview(sineView)
        }
    }
    

    Output

    enter image description here

    You can play with the graphWidth and amplitude to adjust the graph as you like.