Created
August 15, 2020 15:20
-
-
Save ricardo0100/4e04edae0c8b0dff68bc2fba6ef82bf5 to your computer and use it in GitHub Desktop.
Photo View with zoom and edges control in SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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