Skip to content

Instantly share code, notes, and snippets.

@kylehowells
Created February 25, 2023 23:27
Show Gist options
  • Save kylehowells/9681243715760e06ae276ad50f40789c to your computer and use it in GitHub Desktop.
Save kylehowells/9681243715760e06ae276ad50f40789c to your computer and use it in GitHub Desktop.

Revisions

  1. kylehowells created this gist Feb 25, 2023.
    112 changes: 112 additions & 0 deletions WaveView.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@
    import UIKit

    // MARK: - WaveView

    class WaveView: UIView {

    private var shapeLayer: CAShapeLayer = CAShapeLayer()

    // MARK: - Animation Properties

    /// 0 - Static
    /// 20 - Very fast
    var speed: CGFloat = 10

    /// Wave Height
    var frequency: CGFloat = 6.0

    /// Wave phase
    private var phase: CGFloat = 0.0

    /// Wave color
    var preferredColor: UIColor = UIColor.cyan {
    didSet {
    self.shapeLayer.fillColor = self.preferredColor.cgColor
    self.shapeLayer.strokeColor = self.preferredColor.cgColor
    }
    }

    // MARK: - Start Animation

    enum Direction {
    case right
    case left
    }

    func animationStart(direction: Direction, speed: Double) {
    self.preferredColor = UIColor.cyan
    self.layer.addSublayer(self.shapeLayer)

    if direction == .right {
    self.speed = -speed
    } else {
    self.speed = speed
    }

    self.startDisplayLink()
    }


    // MARK: - Display Link

    private weak var displayLink: CADisplayLink?

    private var startTime: CFTimeInterval = 0

    private func startDisplayLink() {
    self.startTime = CACurrentMediaTime()
    self.displayLink?.invalidate()

    let displayLink = CADisplayLink(target: self, selector:#selector(self.handleDisplayLink(_:)))
    displayLink.add(to: .main, forMode: .common)
    self.displayLink = displayLink
    }

    @objc private func handleDisplayLink(_ displayLink: CADisplayLink) {
    self.phase = (CACurrentMediaTime() - self.startTime) * self.speed

    self.updatePath()
    }

    private func stopDisplayLink() {
    self.displayLink?.invalidate()
    }


    // MARK: - Layout

    override func layoutSubviews() {
    super.layoutSubviews()

    self.shapeLayer.frame = self.bounds
    self.updatePath()
    }

    func updatePath() {
    let path: UIBezierPath = UIBezierPath()
    let width: CGFloat = self.bounds.width
    let height: CGFloat = self.bounds.height

    let mid: CGFloat = height * 0.25

    let waveLength: CGFloat = width / self.frequency
    let waveHeightCoef: CGFloat = self.frequency

    path.move(to: CGPoint(x: 0, y: self.bounds.maxY))
    path.addLine(to: CGPoint(x: 0, y: mid))

    for x in stride(from: 0, through: width, by: 1)
    {
    let actualX: CGFloat = x / waveLength
    let sine: CGFloat = -sin((actualX + self.phase))

    let y: CGFloat = waveHeightCoef * sine + mid
    path.addLine(to: CGPoint(x: x, y: y))
    }

    path.addLine(to: CGPoint(x: CGFloat(width), y: self.bounds.maxY))
    path.addLine(to: CGPoint(x: 0, y: self.bounds.maxY))

    self.shapeLayer.path = path.cgPath
    }
    }