Skip to content

Instantly share code, notes, and snippets.

@ricardo0100
Created August 15, 2020 15:20
Show Gist options
  • Save ricardo0100/4e04edae0c8b0dff68bc2fba6ef82bf5 to your computer and use it in GitHub Desktop.
Save ricardo0100/4e04edae0c8b0dff68bc2fba6ef82bf5 to your computer and use it in GitHub Desktop.
Photo View with zoom and edges control in SwiftUI
import SwiftUI
struct PhotoView: View {
@State var scale: CGFloat = 1
@State var scaleAnchor: UnitPoint = .center
@State var lastScale: CGFloat = 1
@State var offset: CGSize = .zero
@State var lastOffset: CGSize = .zero
@State var debug = ""
let image: UIImage
var body: some View {
GeometryReader { geometry in
let magnificationGesture = MagnificationGesture()
.onChanged{ gesture in
scaleAnchor = .center
scale = lastScale * gesture
}
.onEnded { _ in
fixOffsetAndScale(geometry: geometry)
}
let dragGesture = DragGesture()
.onChanged { gesture in
var newOffset = lastOffset
newOffset.width += gesture.translation.width
newOffset.height += gesture.translation.height
offset = newOffset
}
.onEnded { _ in
fixOffsetAndScale(geometry: geometry)
}
Image(uiImage: image)
.resizable()
.scaledToFit()
.position(x: geometry.size.width / 2,
y: geometry.size.height / 2)
.scaleEffect(scale, anchor: scaleAnchor)
.offset(offset)
.gesture(dragGesture)
.gesture(magnificationGesture)
}
.background(Color.black)
.edgesIgnoringSafeArea(.all)
}
private func fixOffsetAndScale(geometry: GeometryProxy) {
let newScale: CGFloat = .minimum(.maximum(scale, 1), 4)
let screenSize = geometry.size
let originalScale = image.size.width / image.size.height >= screenSize.width / screenSize.height ?
geometry.size.width / image.size.width :
geometry.size.height / image.size.height
let imageWidth = (image.size.width * originalScale) * newScale
var width: CGFloat = .zero
if imageWidth > screenSize.width {
let widthLimit: CGFloat = imageWidth > screenSize.width ?
(imageWidth - screenSize.width) / 2
: 0
width = offset.width > 0 ?
.minimum(widthLimit, offset.width) :
.maximum(-widthLimit, offset.width)
}
let imageHeight = (image.size.height * originalScale) * newScale
var height: CGFloat = .zero
if imageHeight > screenSize.height {
let heightLimit: CGFloat = imageHeight > screenSize.height ?
(imageHeight - screenSize.height) / 2
: 0
height = offset.height > 0 ?
.minimum(heightLimit, offset.height) :
.maximum(-heightLimit, offset.height)
}
let newOffset = CGSize(width: width, height: height)
lastScale = newScale
lastOffset = newOffset
withAnimation() {
offset = newOffset
scale = newScale
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment