Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI AspectRatio does not work for images from camera

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?

like image 611
Andreas Schultz Avatar asked Oct 12 '19 16:10

Andreas Schultz


People also ask

How do I fit an image in SwiftUI?

To make an image scales to fit the current view, we use the resizable() modifier, which resizes an image to fit available space. We can see the whole image now, but it got stretched out and losing its aspect ratio, which is usually not the behavior we want. The image view resize to fit available space.

How do I change the width and height of an image in Swift?

Show activity on this post. extension UIImage { func imageResize (sizeChange:CGSize)-> UIImage{ let hasAlpha = true let scale: CGFloat = 0.0 // Use scale factor of main screen UIGraphicsBeginImageContextWithOptions(sizeChange, ! hasAlpha, scale) self. draw(in: CGRect(origin: CGPoint.


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!
    }
like image 103
Vincent Garcia Avatar answered Oct 07 '22 07:10

Vincent Garcia


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)
    }
}
like image 42
ChuckZHB Avatar answered Oct 07 '22 06:10

ChuckZHB


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!
}
like image 35
ninjahamster Avatar answered Oct 07 '22 06:10

ninjahamster