SwiftUI AspectRatio does not work for images from camera

Viewed 1687

I'm not sure if this a SwiftUI specific issue. Anyway, I'm have an UIImagePickerController that I implemented into a SwiftUI view by using the UIViewControllerRepresentableProtocol:

struct ContentView: View {

@State var showCameraView = false
@State var showImagePicker = false
@State var UserImage = Image("user")

var body: some View {
    VStack {
        UserImage
            .resizable()
            .frame(width: 200, height: 200)
            .scaledToFit()
            .background(Color.gray)
            .cornerRadius(200)
            .clipped()
        Button(action: {self.showImagePicker = true}) {
            Text("Choose from camera roll")
        }
            .padding(.top, 10)
    }
        .sheet(isPresented: $showImagePicker) {
            ImagePicker(showImagePicker: self.$showImagePicker, pickedImage: self.$UserImage)
            }

}

}

struct ImagePicker: UIViewControllerRepresentable {

@Binding var showImagePicker: Bool
@Binding var pickedImage: Image

func makeCoordinator() -> ImagePicker.Coordinator {
    Coordinator(self)
}

func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
    let imagePicker = UIImagePickerController()
    imagePicker.delegate = context.coordinator
    return imagePicker
}

func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
    return
}

class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    var parent: ImagePicker

    init(_ imagePicker: ImagePicker) {
        self.parent = imagePicker
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
        parent.pickedImage = Image(uiImage: uiImage)
        parent.showImagePicker = false
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        parent.showImagePicker = false
    }
}

}

It works fine when picking images that are not taken by the device's camera. However, every time I pick an Image that was taken by the camera itself, it seems that the .aspectRatio modifier does not get applied because the loaded Image's dimensions are distorted in this case. Does anybody spot something wrong in my code or know a solution?

3 Answers

Had the same issue. I used a modified version of the one proposed by @ninjahamster where I simply didn't resize the image. Any other solution is welcome.

    func resizeImage(image: UIImage) -> UIImage {
        let width = image.size.width
        let height = image.size.height
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        image.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return newImage!
    }

I've had the same issue. I didn't find a solution, but a workaround. I resized the image with a function in the coordinator class, bevor I return it to the view. Works for me.

func resizeImage(image: UIImage) -> UIImage {
    let scale = 300 / image.size.width
    let newHeight = image.size.height * scale
    UIGraphicsBeginImageContext(CGSize(width: 300, height: newHeight))
    image.draw(in: CGRect(x: 0, y: 0, width: 300, height: newHeight))
    let newImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return newImage!
}

I met the same issue. I didn't realize it is a lacking from SwiftUI!! Now I have applied ninjahamster's workaround.

My code here, in case anyone need some reference. See the changes in func didFinishPickingMediaWithInfo.

import SwiftUI

final class ImagePickerCoordinator: NSObject {
    @Binding var image: UIImage?
    @Binding var takePhoto: Bool

    init(image: Binding<UIImage?>, takePhoto: Binding<Bool>) {
        _image = image
        _takePhoto = takePhoto
    }
}

struct ImagePicker: UIViewControllerRepresentable {
    @Binding var image: UIImage?
    @Binding var takePhoto: Bool

    func makeCoordinator() -> ImagePickerCoordinator {
        ImagePickerCoordinator(image: $image, takePhoto: $takePhoto)
    }

    func makeUIViewController(context: Context) -> UIImagePickerController {
        let pickerController = UIImagePickerController()
        pickerController.delegate = context.coordinator
        return pickerController
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
        switch self.takePhoto {
        case true:
            uiViewController.sourceType = .camera
            uiViewController.showsCameraControls = true
        case false:
            uiViewController.sourceType = .photoLibrary
        }
        uiViewController.allowsEditing = false
    }
}

extension ImagePickerCoordinator: UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        if let imageOriginal = info[.originalImage] as? UIImage {
//            image = imageOriginal
            image = resizeImage(image: imageOriginal)
        }
        if let imageEdited = info[.editedImage] as? UIImage {
            image = imageEdited
        }

        picker.dismiss(animated: true, completion: nil)
    }

    func resizeImage(image: UIImage) -> UIImage {
        let width = image.size.width
        let height = image.size.height
        UIGraphicsBeginImageContext(CGSize(width: width, height: height))
        image.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        picker.dismiss(animated: true, completion: nil)
    }
}
Related