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?
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.
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.
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 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)
}
}
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!
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With