Skip to content

Instantly share code, notes, and snippets.

@mingsai
Created July 3, 2015 00:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mingsai/ffd07a47891ec781cf78 to your computer and use it in GitHub Desktop.
Save mingsai/ffd07a47891ec781cf78 to your computer and use it in GitHub Desktop.
A swift waveform custom view to create a sin wave relative to audio data being sent from a UIViewController
//
// MNGWaveformView.swift
//
//
// Created by Tommie Carter on 6/29/15.
// Copyright © 2015 MING Technology. All rights reserved.
//
import UIKit
@IBDesignable
class MNGWaveformView:UIView {
/*
* The total number of waves
* Default: 5
*/
@IBInspectable var numberOfWaves = 5
/*
* Color to use when drawing the waves
* Default: white
*/
@IBInspectable var waveColor = UIColor.whiteColor()
/*
* Line width used for the proeminent wave
* Default: 3.0f
*/
@IBInspectable var primaryWaveLineWidth:CGFloat = 3.0
/*
* Line width used for all secondary waves
* Default: 1.0f
*/
@IBInspectable var secondaryWaveLineWidth:CGFloat = 1.0
/*
* The amplitude that is used when the incoming amplitude is near zero.
* Setting a value greater 0 provides a more vivid visualization.
* Default: 0.01
*/
@IBInspectable var idleAmplitude:CGFloat = 0.01
/*
* The frequency of the sinus wave. The higher the value, the more sinus wave peaks you will have.
* Default: 1.5
*/
@IBInspectable var frequency:CGFloat = 1.5
/*
*
* The current amplitude
*/
var amplitude:CGFloat = 0
/*
* The phase shift that will be applied with each level setting
* Change this to modify the animation speed or direction
* Default: -0.15
*/
@IBInspectable var phaseShift:CGFloat = -0.15
/*
* The lines are joined stepwise, the more dense you draw, the more CPU power is used.
* Default: 5.0
*/
@IBInspectable var density:CGFloat = 5.0
private var phase:CGFloat = 0.0
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
/*
* Tells the waveform to redraw itself using the given level (normalized value)
*/
func updateWithLevel (level:CGFloat) {
self.phase += self.phaseShift
self.amplitude = fmax( level, self.idleAmplitude)
//delay the next call
let delayTime = dispatch_time(DISPATCH_TIME_NOW,
Int64(1.99 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
self.setNeedsDisplay()
}
}
func setup() {
self.frequency = 3.0
self.amplitude = 0.2
self.idleAmplitude = 0.1
self.numberOfWaves = 5
self.phaseShift = -0.15
self.density = 5.0
self.waveColor = UIColor.whiteColor()
self.primaryWaveLineWidth = 2.5
self.secondaryWaveLineWidth = 1.0
}
override func drawRect(rect: CGRect) {
super.drawRect(rect)
let context = UIGraphicsGetCurrentContext()
CGContextClearRect(context, self.bounds)
self.backgroundColor?.set()
CGContextFillRect(context, rect);
// We draw multiple sinus waves, with equal phases but altered amplitudes, multiplied by a parable function.
for(var i=0; i < self.numberOfWaves; i++) {
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, (i==0 ? self.primaryWaveLineWidth : self.secondaryWaveLineWidth));
let halfHeight = CGRectGetHeight(self.bounds) / 2.0
let width = CGRectGetWidth(self.bounds)
let mid = width / 2.0
let maxAmplitude = halfHeight / 4.0 // 4 corresponds to twice the stroke width
// Progress is a value between 1.0 and -0.5, determined by the current wave idx, which is used to alter the wave's amplitude.
let progress = 1.0 - Float(i) / Float(self.numberOfWaves)
let normedAmplitude = Float(1.5) * progress - 0.5 * Float(self.amplitude)
let multiplier = min(1.0, (progress / 3.0 * 2.0) + (1.0 / 3.0))
self.waveColor.colorWithAlphaComponent(CGFloat(multiplier) * CGColorGetAlpha(self.waveColor.CGColor)).set()
for(var x = 0; x < Int(width + self.density); x += Int(self.density)) {
// We use a parable to scale the sinus wave, that has its peak in the middle of the view.
let scaling:CGFloat = -pow(1 / mid * (CGFloat(x) - mid), 2) + 1
let y = Float(scaling) * Float(maxAmplitude) * Float(normedAmplitude) * sinf(Float(2) * Float(M_PI) * ( Float(x) / Float(width) * Float(self.frequency) + Float(self.phase))) + Float(halfHeight)
if (x == 0) {
CGContextMoveToPoint(context, CGFloat(x), CGFloat(y))
}
else {
CGContextAddLineToPoint(context, CGFloat(x), CGFloat(y));
}
}
CGContextStrokePath(context);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment