add an option to prevent scrolling past the end of the audio
When zoomed in, you can swipe the view until the last part of the waveform is on the left edge of the screen. This seems a bit disorienting to me. It would be nice to have an option so that the end of the waveform stays at the right edge of the screen.
This is not intended behavior and sounds like a bug.
I fixed it locally by:
@objc func handlePinchGesture(_ recognizer: UIPinchGestureRecognizer) {
if !doesAllowStretch {
return
}
if recognizer.scale == 1 {
return
}
setScale(scale: scale * recognizer.scale, fixedX: recognizer.location(in: self).x/bounds.width)
recognizer.scale = 1
}
private var scale:CGFloat = 1
func setScale(scale:CGFloat, fixedX:CGFloat) {
if scale < 1
{
return
}
self.scale = scale
let zoomSamplesCount = CGFloat(zoomSamples.count)
let pinchCenterSample = zoomSamples.lowerBound + Int(zoomSamplesCount * fixedX)
let newZoomSamplesCount = CGFloat(totalSamples)/scale
var newZoomStart = pinchCenterSample - Int(newZoomSamplesCount * fixedX )
var newZoomEnd = newZoomStart + Int(newZoomSamplesCount)
if newZoomEnd > totalSamples
{
newZoomStart = totalSamples - Int(newZoomSamplesCount)
newZoomEnd = totalSamples
}
zoomSamples = (newZoomStart ..< newZoomEnd).clamped(to: 0 ..< totalSamples)
}
override open func layoutSubviews() {
super.layoutSubviews()
guard audioContext != nil && !zoomSamples.isEmpty else {
return
}
switch cacheStatus() {
case .dirty:
renderWaveform()
return
case .notDirty(let cancelInProgressRenderOperation):
if cancelInProgressRenderOperation {
inProgressWaveformRenderOperation = nil
}
}
let childFrame : CGRect
if let cachedSampleRange = cachedWaveformRenderOperation?.sampleRange, !cachedSampleRange.isEmpty , !zoomSamples.isEmpty
{
let cacheScale = CGFloat(cachedSampleRange.count)/CGFloat(zoomSamples.count)
let width = frame.width * cacheScale
let x = CGFloat(cachedSampleRange.lowerBound-zoomSamples.lowerBound)*width/CGFloat(cachedSampleRange.count)
childFrame = CGRect(x: x, y: 0, width: width, height: frame.height)
}
else
{
childFrame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
}
imageView.frame = childFrame
highlightedImage.frame = childFrame
//clipping.frame = CGRect(x: frame.width * scaledHighlightedX, y: 0, width: frame.width * scaledHighlightedWidth, height: frame.height)
clipping.isHidden = !(highlightedSamples?.overlaps(zoomSamples) ?? false)
print("layed-out frames: \(frame) -- \(imageView.frame)")
}
Note: clipping.frame is not set
Thanks for sharing. Does this work for anyone else?
Also related: #96
I was experiencing this in a consumer app because I'm manually placing some additional controls over the waveform, and it seemed like my math was ok but the handles still would end up in the wrong place when zooming/scrolling the waveform (and otherwise ok, but that proves little since the math isn't used when at zero zoom).
I tried the above fix, but it clearly is not the issue since all it does is not apply the frame changes to the clipping view, which is only for the highlighted frames functionality. You can see this by commenting the above line, building the test app, and scrubbing. You'll see that the scrub and zoom are now no longer in sync.
I believe the underlying issue here is not that it is being scrolled outside of the view, but that the render cache is wrong and it's not being re-rendered often enough. In the demo app, you can see the waveform sometimes "catch up" to the current (or perhaps just a recent) render. I'll be playing with with spare time to see if I can find a reasonable fix.
I started testing this, and it seems to be at least one very old issue - the simple repro case I'm working with is that when you are zoomed in, the waveform should not compress when panning. I figured it once worked and regressed, but trying to bisect the issue got me all the way back to before v1.0, and I could reproduce on every version that I could see a waveform in (I stopped testing at 9ba0b654e988c25b01e21a3d43bc6b178d06804a, which I couldn't get to render anything). Given that, I think this is less of a bug and more of a fundamental logic issue. I suspect that zoom functionality will need to be rewritten or heavily refactored to be accurate, and I do not have reason to believe that it works in any version of this lib - I'd be happy to help review or debug further as this library would save me a lot of time if it worked!
Hello, checking in to see if anybody here has solved this issue for themselves and could share back the solution as a PR?