summaryrefslogtreecommitdiff
path: root/Sora/Views/InteractiveImageView.swift
diff options
context:
space:
mode:
authorFuwn <[email protected]>2025-02-22 07:07:57 -0800
committerFuwn <[email protected]>2025-02-22 07:07:57 -0800
commitcafece91bae45194d64f4932bb04be018b82d21b (patch)
tree02d727a13fe530550e3b29b28b7ee9980262eef2 /Sora/Views/InteractiveImageView.swift
parentfeat: Development commit (diff)
downloadsora-testing-cafece91bae45194d64f4932bb04be018b82d21b.tar.xz
sora-testing-cafece91bae45194d64f4932bb04be018b82d21b.zip
feat: Development commit
Diffstat (limited to 'Sora/Views/InteractiveImageView.swift')
-rw-r--r--Sora/Views/InteractiveImageView.swift89
1 files changed, 89 insertions, 0 deletions
diff --git a/Sora/Views/InteractiveImageView.swift b/Sora/Views/InteractiveImageView.swift
new file mode 100644
index 0000000..7afde7d
--- /dev/null
+++ b/Sora/Views/InteractiveImageView.swift
@@ -0,0 +1,89 @@
+import SwiftUI
+
+struct InteractiveImageView: View {
+ let image: Image
+ @State private var screenWidth = 0.0
+ @State private var screenHeight = 0.0
+ @State private var currentScale = 1.0
+ @State private var previousScale = 0.0
+ @State private var currentOffset: CGSize = .zero
+ @State private var previousOffset: CGSize = .zero
+
+ var body: some View {
+ GeometryReader { geometry in
+ VStack {
+ image
+ .resizable()
+ .scaledToFit()
+ .scaleEffect(currentScale)
+ .offset(currentOffset)
+ .frame(width: screenWidth, height: screenHeight)
+ .clipped()
+ .gesture(
+ MagnifyGesture()
+ .onChanged { gesture in
+ withAnimation(.interactiveSpring()) {
+ currentScale =
+ previousScale + gesture.magnification - (previousScale == 0 ? 0 : 1)
+ }
+ }
+ .onEnded { _ in
+ previousScale = currentScale
+ }
+ .simultaneously(
+ with: DragGesture(minimumDistance: 0)
+ .onChanged { gesture in
+ withAnimation(.interactiveSpring()) {
+ var newOffset: CGSize = .zero
+ let offset = gesture.translation
+
+ newOffset.width = offset.width + previousOffset.width
+ newOffset.height = offset.height + previousOffset.height
+
+ currentOffset = clampOffset(offset: newOffset)
+ }
+ }
+ .onEnded { _ in
+ previousOffset = currentOffset
+ }
+ )
+ )
+ .highPriorityGesture(
+ TapGesture(count: 2)
+ .onEnded {
+ withAnimation {
+ currentScale = 1.0
+ previousScale = 0
+ currentOffset = .zero
+ previousOffset = .zero
+ }
+ }
+ )
+ }
+ .onAppear {
+ screenWidth = geometry.size.width
+ screenHeight = geometry.size.height
+ }
+ }
+ }
+
+ private func clampOffset(offset: CGSize = .zero) -> CGSize {
+ var newOffset = offset
+
+ if currentScale > 1 {
+ let maxX = ((screenWidth * currentScale) - screenWidth) / 2
+ let maxY = ((screenHeight * currentScale) - screenHeight) / 2
+
+ newOffset.width = min(max(-maxX, newOffset.width), maxX)
+ newOffset.height = min(max(-maxY, newOffset.height), maxY)
+ } else {
+ newOffset = .zero
+ }
+
+ return newOffset
+ }
+}
+
+#Preview {
+ InteractiveImageView(image: Image(systemName: "photo"))
+}