62 lines
1.8 KiB
Swift
62 lines
1.8 KiB
Swift
import SwiftUI
|
|
|
|
struct PlotContainer<Content: View>: View {
|
|
let title: String
|
|
@ViewBuilder let content: () -> Content
|
|
|
|
@State private var scale: CGFloat = 1.0
|
|
@State private var offset: CGSize = .zero
|
|
@State private var lastOffset: CGSize = .zero
|
|
|
|
var body: some View {
|
|
GeometryReader { geo in
|
|
content()
|
|
.scaleEffect(scale)
|
|
.offset(offset)
|
|
.gesture(dragGesture)
|
|
.gesture(magnificationGesture)
|
|
.simultaneousGesture(doubleTapReset)
|
|
.clipped()
|
|
.overlay(alignment: .topLeading) {
|
|
if !title.isEmpty {
|
|
Text(title)
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
.padding(4)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var dragGesture: some Gesture {
|
|
DragGesture()
|
|
.onChanged { value in
|
|
offset = CGSize(
|
|
width: lastOffset.width + value.translation.width,
|
|
height: lastOffset.height + value.translation.height
|
|
)
|
|
}
|
|
.onEnded { _ in
|
|
lastOffset = offset
|
|
}
|
|
}
|
|
|
|
private var magnificationGesture: some Gesture {
|
|
MagnifyGesture()
|
|
.onChanged { value in
|
|
scale = max(0.5, min(10.0, value.magnification))
|
|
}
|
|
}
|
|
|
|
private var doubleTapReset: some Gesture {
|
|
TapGesture(count: 2)
|
|
.onEnded {
|
|
withAnimation(.easeOut(duration: 0.2)) {
|
|
scale = 1.0
|
|
offset = .zero
|
|
lastOffset = .zero
|
|
}
|
|
}
|
|
}
|
|
}
|