I am trying to build a video editing timeline using SwiftUI which consists of a series of trimmers (sample code for trimmer below). I plan to embed multiple of these trimmers in an HStack
to make an editing timeline (HStack
in turn will be embedded in a ScrollView). What I want to achieve is that if I trim end of any of these trimmers by dragging it's left/right edge, the other trimmers on timeline should move left/right to fill the gap. I understand as the view shrinks/expands during trimming, there might be a need for spacers on the edges of HStack
whose widths need to be adjusted while trimming is ongoing.
Right now the code I have for the trimmer uses a fixed frameWidth of the view, so the view occupies full space even if the trimming ends move. It's not clear how to modify my code below to achieve what I want. This was pretty much possible to do in UIKit by the way.
import SwiftUI
struct SimpleTrimmer: View {
@State var frameWidth:CGFloat = 300
let minWidth: CGFloat = 30
@State private var leftOffset: CGFloat = 0
@State private var rightOffset: CGFloat = 0
@GestureState private var leftDragOffset: CGFloat = 0
@GestureState private var rightDragOffset: CGFloat = 0
private var leftAdjustment: CGFloat {
// NSLog("Left offset \(leftOffset + leftDragOffset)")
var adjustment = max(0, leftOffset + leftDragOffset)
if frameWidth - rightOffset - adjustment - 60 < minWidth {
adjustment = frameWidth - rightOffset - minWidth - 60.0
}
return adjustment
}
private var rightAdjustment: CGFloat {
var adjustment = max(0, rightOffset - rightDragOffset)
if frameWidth - adjustment - leftOffset - 60 < minWidth {
adjustment = frameWidth - leftOffset - 60 - minWidth
}
return adjustment
}
var body: some View {
HStack(spacing: 10) {
Image(systemName: "chevron.compact.left")
.frame(width: 30, height: 70)
.background(Color.blue)
.offset(x: leftAdjustment)
.gesture(
DragGesture(minimumDistance: 0)
.updating($leftDragOffset) { value, state, trans in
state = value.translation.width
}
.onEnded { value in
var maxLeftOffset = max(0, leftOffset + value.translation.width)
if frameWidth - rightAdjustment - maxLeftOffset - 60 < minWidth {
maxLeftOffset = frameWidth - rightAdjustment - minWidth - 60
}
leftOffset = maxLeftOffset
}
)
Spacer()
Image(systemName: "chevron.compact.right")
.frame(width: 30, height: 70)
.background(Color.blue)
.offset(x: -rightAdjustment)
.gesture(
DragGesture(minimumDistance: 0)
.updating($rightDragOffset) { value, state, trans in
state = value.translation.width
}
.onEnded { value in
var minRightOffset = max(0, rightOffset - value.translation.width)
if minRightOffset < leftAdjustment - 60 - minWidth {
minRightOffset = leftAdjustment - 60 - minWidth
}
rightOffset = minRightOffset
}
)
}
.foregroundColor(.black)
.font(.title3.weight(.semibold))
.padding(.horizontal, 7)
.padding(.vertical, 3)
.background {
RoundedRectangle(cornerRadius: 7)
.fill(.yellow)
.padding(.leading, leftAdjustment)
.padding(.trailing, rightAdjustment)
}
.frame(width: frameWidth)
}
}
#Preview {
SimpleTrimmer()
}