In my app, I give the users the option to press on images to show them full screen. When the image is displayed in full screen users can zoom in on it and should be able to drag it around to see other portions of the image.
Currently when the image is zoomed in and I try to drag it to any side it re-centers and doesn't drag well. Also I can only zoom into the center. How can I modify this code so that the drag gesture to any side when zoomed in is smooth and a zoom gesture can be done on the sides of the image not just the center?
The KFImage is a simple package that renders images from a url. It can be added to Xcode with this url: https://github.com/onevcat/Kingfisher.git.
import SwiftUI
import Kingfisher
import UIKit
struct ContentView: View {
@State private var myPhoto = "https://firebasestorage.googleapis.com:443/v0/b/hustle-85b6c.appspot.com/o/messages%2F7AC5914A-6239-41CF-85EF-E1C0F25C0A84?alt=media&token=8720789b-7cdd-410c-9532-143a2bcf3f3b"
@State private var currentScale: CGFloat = 1.0
@State private var previousScale: CGFloat = 1.0
@State private var currentOffset = CGSize.zero
@State private var previousOffset = CGSize.zero
@State var imageHeight: Double = 0.0
@State var imageWidth: Double = 0.0
var body: some View {
ZStack {
Rectangle().foregroundColor(.black)
GeometryReader { geometry in
KFImage(URL(string: myPhoto))
.resizable()
.aspectRatio(contentMode: .fit)
.scaleEffect(max(self.currentScale, 1.0))
.offset(x: self.currentOffset.width + (widthOrHeight(width: true) - imageWidth) / 2.0, y: self.currentOffset.height + (widthOrHeight(width: false) - imageHeight) / 2.0)
.background ( /// this background is to center the image (image loaded async so have to wait for it to load and then get height)
GeometryReader { proxy in
Color.clear
.onChange(of: proxy.size.height) { _ in
imageHeight = proxy.size.height
imageWidth = proxy.size.width
}
}
)
.gesture(
SimultaneousGesture(
DragGesture()
.onChanged { value in
let deltaX = value.translation.width - self.previousOffset.width
let deltaY = value.translation.height - self.previousOffset.height
previousOffset.width = value.translation.width
previousOffset.height = value.translation.height
let newOffsetWidth = self.currentOffset.width + deltaX / self.currentScale
let newOffsetHeight = self.currentOffset.height + deltaY / self.currentScale
withAnimation(.linear(duration: 0.25)){
if abs(newOffsetWidth) <= geometry.size.width - 200.0 && abs(newOffsetWidth) >= -200.0 {
self.currentOffset.width = newOffsetWidth
}
if abs(newOffsetHeight) < 450 {
self.currentOffset.height = newOffsetHeight
}
}
}
.onEnded { _ in
withAnimation(.easeInOut){
if currentScale < 1.2 {
self.previousOffset = CGSize.zero
self.currentOffset = CGSize.zero
}
}
},
MagnificationGesture().onChanged { value in
let delta = value / self.previousScale
let newScale = self.currentScale * delta
withAnimation {
if newScale <= 3.5 {
self.previousScale = value
self.currentScale = newScale
}
if newScale < 1.3 {
self.previousScale = 1.0
self.currentScale = 1.0
self.previousOffset = CGSize.zero
self.currentOffset = CGSize.zero
}
}
}
)
)
}
}.ignoresSafeArea()
}
}