FDWaveformView
FDWaveformView displays audio waveforms in Swift apps so users can preview audio, scrub, and pick positions with ease.
:hatching_chick: Virtual tip jar:
Usage
Add an FDWaveformView programmatically, then load audio. If your file is missing an extension, see the Stack Overflow answer on AVURLAsset without extensions.
let thisBundle = Bundle(for: type(of: self))
let url = thisBundle.url(forResource: "Submarine", withExtension: "aiff")
self.waveform.audioURL = url
!Waveform overview showing loaded audio
Features
Highlight playback
Highlight a portion of the waveform to show progress.
self.waveform.highlightedSamples = 0..<(self.waveform.totalSamples / 2)
!Waveform with highlighted progress
Zoom for detail
Render only the visible portion while progressively adding detail as you zoom.
self.waveform.zoomSamples = 0..<(self.waveform.totalSamples / 4)
Gesture control
Allow scrubbing, stretching, and scrolling with built-in gestures.
self.waveform.doesAllowScrubbing = true
self.waveform.doesAllowStretch = true
self.waveform.doesAllowScroll = true
!Gesture-driven waveform interaction
Animated updates
Animate property changes for smoother UI feedback.
UIView.animate(withDuration: 0.3) {
let randomNumber = arc4random() % self.waveform.totalSamples
self.waveform.highlightedSamples = 0 ..< randomNumber
}
!Animated waveform highlight change
Rendering quality
- Antialiased waveforms draw extra pixels to avoid jagged edges.
- Autolayout-driven size changes trigger re-rendering to prevent pixelation.
- Supports iOS 15+, visionOS 1+, and Swift tools version 5.10, as declared in
Package.swift. - Includes unit tests that run on GitHub Actions.
Installation
Add this package with Swift Package Manager. In Xcode that is File > Add Package Dependencies...
API
Following is the complete API for this module:
FDWaveformView(open class, subclass ofUIView)
init() (public init) default initializer
- delegate: FDWaveformViewDelegate? (open var, get/set) delegate for loading and rendering callbacks
- audioURL: URL? (open var, get/set) audio file to render asynchronously
- totalSamples: Int (open var, get) sample count of the loaded asset
- highlightedSamples: CountableRange? (open var, get/set) range tinted with progressColor
- zoomSamples: CountableRange (open var, get/set) range currently displayed
- doesAllowScrubbing: Bool (open var, get/set) enable tap and pan scrubbing
- doesAllowStretch: Bool (open var, get/set) enable pinch-to-zoom
- doesAllowScroll: Bool (open var, get/set) enable panning across the waveform
- wavesColor: UIColor (open var, get/set) tint for the base waveform image
- progressColor: UIColor (open var, get/set) tint for the highlighted waveform
- loadingInProgress: Bool (open var, get) indicates async load in progress
FDWaveformViewDelegate(@objc public protocol)
waveformViewWillRender(_ waveformView: FDWaveformView) (optional)
- waveformViewDidRender(_ waveformView: FDWaveformView) (optional)
- waveformViewWillLoad(_ waveformView: FDWaveformView) (optional)
- waveformViewDidLoad(_ waveformView: FDWaveformView) (optional)
- waveformDidBeginPanning(_ waveformView: FDWaveformView) (optional)
- waveformDidEndPanning(_ waveformView: FDWaveformView) (optional)
- waveformDidEndScrubbing(_ waveformView: FDWaveformView) (optional)
A couple other things are exposed that we do not consider public API:
FDWaveformView(implementsUIGestureRecognizerDelegate)
gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) -> Bool
Testing
GitHub Actions runs the suite on the macOS 15 image with Xcode 16.4. That image provides an iPhone 16 simulator on iOS 18.5. The command is:
xcodebuild test -scheme FDWaveformView -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.5'
On another Xcode, list the simulators that copy of Xcode can see and pass one of those ids. xcodebuild only accepts destinations from the selected Xcode, so an id printed by a different install will fail.
xcrun simctl list devices available | grep iPhone
xcodebuild test -scheme FDWaveformView -destination 'id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
The Example app target deploys to iOS 18.6, so its simulator has to be iOS 18.6 or newer:
xcodebuild build -project Example/Example.xcodeproj -scheme Example -destination 'id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
Contributing
- This project's layout is based on
- Ignore rules are inlined from Swift.gitignore and Global/Xcode.gitignore. The macOS and secret rules above them come from project-template.