I simply began my journey to study coding and doing it on the iPad with Swift playground.
I’ve an audio waveform to which I apply zoom and pan gestures. I’m having a little bit of hassle. Once I zoom in and pan all the way in which to the tip of the waveform, if I strive zooming out, the waveform tends to pan as an alternative, and it disappears. As a substitute of the tip of the waveform being hooked up to the background traces, I can see it shifting. Might you additionally assist implement a method to zoom between my finger?
Here’s a YouTube of the problem https://youtu.be/RrP39Fv-_oc?si=sca_ys0vOnihLFGs and right here is the code:
import SwiftUI
struct WaveformView: View {
@Binding var waveform: [Float]
@State personal var zoomFactor: CGFloat = 1.0
@State personal var lastZoomFactor: CGFloat = 1.0
@State personal var offset: CGSize = .zero
@State personal var lastOffset: CGSize = .zero
@State personal var selectionStart: CGFloat = 0.0
@State personal var selectionEnd: CGFloat = 1.0
var physique: some View {
ZStack {
Coloration.grey.opacity(0.3).edgesIgnoringSafeArea(.all)
GeometryReader { geometry in
ZStack(alignment: .main) {
Canvas { context, measurement in
let middleY = measurement.top / 2
let maxAmplitude = measurement.top / 3 - 1
var path = Path()
path.transfer(to: CGPoint(x: 0, y: middleY))
var previousPoint = CGPoint(x: 0, y: middleY)
for (index, pattern) in waveform.enumerated() {
let x = (measurement.width * CGFloat(index) / CGFloat(waveform.depend)) * zoomFactor - offset.width
let amplitude = max(min(CGFloat(pattern) * maxAmplitude, maxAmplitude), -maxAmplitude)
let level = CGPoint(x: x, y: middleY - amplitude)
let controlPoint1 = CGPoint(x: (previousPoint.x + level.x) / 2, y: previousPoint.y)
let controlPoint2 = CGPoint(x: (previousPoint.x + level.x) / 2, y: level.y)
path.addCurve(to: level, control1: controlPoint1, control2: controlPoint2)
previousPoint = level
}
context.stroke(path, with: .colour(.blue), lineWidth: 1)
// Playhead line
}
// Choice overlay and handles
Rectangle()
.fill(Coloration.blue.opacity(0.3)) // Choice overlay colour
.body(width: geometry.measurement.width * (selectionEnd - selectionStart), top: geometry.measurement.top)
.offset(x: geometry.measurement.width * selectionStart, y: 0)
Circle() // Begin deal with
.fill(Coloration.blue)
.body(width: 30, top: 30)
.offset(x: geometry.measurement.width * selectionStart - 15, y: geometry.measurement.top / 2 - 15)
.gesture(
DragGesture()
.onChanged { worth in
selectionStart = min(max(0, worth.location.x / geometry.measurement.width), selectionEnd)
}
)
Circle() // Finish deal with
.fill(Coloration.blue)
.body(width: 30, top: 30)
.offset(x: geometry.measurement.width * selectionEnd - 15, y: geometry.measurement.top / 2 - 15)
.gesture(
DragGesture()
.onChanged { worth in
selectionEnd = max(min(1, worth.location.x / geometry.measurement.width), selectionStart)
}
)
}
.gesture(
SimultaneousGesture(
MagnificationGesture().onChanged { worth in
let newZoomFactor = lastZoomFactor * worth
self.zoomFactor = max(1.0, newZoomFactor)
}.onEnded { worth in
if abs(self.zoomFactor - 1.0) < 0.05 {
self.zoomFactor = 1.0
self.offset = .zero
self.lastOffset = .zero
} else {
self.lastZoomFactor = self.zoomFactor
let adjustmentFactor = self.zoomFactor / self.lastZoomFactor
self.offset.width *= adjustmentFactor
self.lastOffset = self.offset
}
},
DragGesture().onChanged { worth in
let proposedOffset = lastOffset.width - worth.translation.width
let maxOffset = max((geometry.measurement.width * zoomFactor) - geometry.measurement.width, 0)
self.offset.width = min(max(proposedOffset, 0), maxOffset)
}.onEnded { worth in
self.lastOffset = self.offset
}
)
)
.onChange(of: waveform) { _ in
resetSelection()
resetZoomAndOffset()
}
.gesture(TapGesture(depend: 2).onEnded {
resetSelection()
resetZoomAndOffset()
})
}
}
}
personal func resetSelection() {
selectionStart = 0.0
selectionEnd = 1.0
}
personal func resetZoomAndOffset() {
zoomFactor = 1.0
lastZoomFactor = 1.0
offset = .zero
lastOffset = .zero
}
}
I zoom in, pan to the tip of waveform, then attempt to zoom out.
Waveform detaches from background body, pans as an alternative of zooming out.