Rin Star
Rin Star

Reputation: 65

How to Prevent Stretched Images When Disabling .scaledToFit() in SwiftUI?

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

Answers (1)

Sweeper
Sweeper

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

Related Questions