Reputation: 65
I'm working on an iOS app using SwiftUI, and I have an issue with image resizing. In my code, I have the following SwiftUI view hierarchy:
//Gestures I'm using to change the image
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)
}
// Function that doing it
private func fixOffsetAndScale(geometry: GeometryProxy) {
let newScale: CGFloat = .minimum(.maximum(scale, 1), 4)
let screenSize = geometry.size
let originalScale = scannedImage.size.width / scannedImage.size.height >= screenSize.width / screenSize.height ?
geometry.size.width / scannedImage.size.width :
geometry.size.height / scannedImage.size.height
let imageWidth = (scannedImage.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 = (scannedImage.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
}
}
// Image itself, with commented out .scaledToFit()
Image(uiImage: scannedImage)
.resizable()
// .scaledToFit()
.border(.red, width: 2)
.position(x: geometry.size.width / 2,
y: geometry.size.height / 2)
.scaleEffect(scale, anchor: scaleAnchor)
.offset(offset)
.gesture(dragGesture)
.gesture(magnificationGesture)
.frame(width: geometry.size.width, height: geometry.size.height)
I've commented out .scaledToFit() because of a different requirement, but now I'm facing an issue where images that are not in A4 format (e.g., square) are stretching to fill the entire screen. This is not the behavior I want, and it results in distorted images.
How can I prevent images from stretching and maintain their original aspect ratio when .scaledToFit() is commented out? Is there a way to achieve this while keeping the other transformations and gestures intact?
Upvotes: 0
Views: 375
Reputation: 273540
It is because you used resizable
and set the frame
to fill the entire available space of the GeometryReader
, so that is what the image does.
Just add .aspectRatio(contentMode: .fit)
if you don't want the stretching. This will still cause the image to resize, but it will maintain its aspect ratio, and only resize to a size that fits the frame
.
Alternatively, you can remove resizable
. scaleEffect
will still work on the image (it works all pretty much every kind of view). The image will be in its original size, so it might not be suitable if the image can be larger than the screen.
Upvotes: 0