Skip to content

Instantly share code, notes, and snippets.

@geraldWilliam
Last active October 22, 2023 10:16
Show Gist options
  • Save geraldWilliam/837845222a3367bf2ce3e03d8bb9c32d to your computer and use it in GitHub Desktop.
Save geraldWilliam/837845222a3367bf2ce3e03d8bb9c32d to your computer and use it in GitHub Desktop.
SwiftUI Zoom
import SwiftUI
struct Zoom: ViewModifier {
@Binding var scale: CGFloat
@Binding var position: CGPoint
@State private var currentZoom: CGFloat = 0
private let scrollCoordinateSpace = "scroll"
func body(content: Content) -> some View {
GeometryReader { geometry in
ScrollView([.vertical, .horizontal]) {
content
.frame(
width: sideLength(geometry, multiplier: currentZoom + scale),
height: sideLength(geometry, multiplier: currentZoom + scale)
)
.gesture(
MagnificationGesture()
.onChanged { scale in
currentZoom = scale - 1
}
.onEnded { _ in
scale += currentZoom
currentZoom = 0.0
}
)
.accessibilityZoomAction { action in
switch action.direction {
case .zoomIn:
scale += 1
case .zoomOut:
scale -= 1
}
}
.onTapGesture(count: 2) {
withAnimation {
// Zoom in / out on double tap
scale = scale > 1 ? 1 : 1.5
}
}
.onTapGesture {
// Empty to prevent dismiss
}
.background(
GeometryReader { geometry -> Color in
Task {
position = geometry.frame(in: .named(scrollCoordinateSpace)).origin
}
return Color.clear
}
)
}
.scrollIndicators(.hidden)
.coordinateSpace(name: scrollCoordinateSpace)
}
}
private func sideLength(_ geometry: GeometryProxy, multiplier: CGFloat = 1) -> CGFloat {
min(geometry.size.width, geometry.size.height) * multiplier
}
}
extension View {
func zoom(scale: Binding<CGFloat>, position: Binding<CGPoint>) -> some View {
modifier(Zoom(scale: scale, position: position))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment