Created
July 3, 2015 00:03
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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