Taimoor Arif
Taimoor Arif

Reputation: 1190

Crop Image From Image Picker in SwiftUI

I am using UIImagePickerController to select image from camera or gallery in my SwiftUI app. I set allowsEditing to true. After setting it to true it is showing a square to select the photo like this:

image selection

I want to select image of size (e.g width = 150 and height = 150) instead of that square. Can I do that in SwiftUI. If yes, how can I achieve this?

This is my code:

import Foundation
import SwiftUI

public struct ImagePickerView: UIViewControllerRepresentable {

    private let sourceType: UIImagePickerController.SourceType
    private let onImagePicked: (UIImage) -> Void
    @Environment(\.presentationMode) private var presentationMode

    public init(sourceType: UIImagePickerController.SourceType, onImagePicked: @escaping (UIImage) -> Void) {
        self.sourceType = sourceType
        self.onImagePicked = onImagePicked
    }

    public func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.sourceType = self.sourceType
        picker.delegate = context.coordinator
        picker.allowsEditing = true
        return picker
    }

    public func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}

    public func makeCoordinator() -> Coordinator {
        Coordinator(
            onDismiss: { self.presentationMode.wrappedValue.dismiss() },
            onImagePicked: self.onImagePicked
        )
    }

    final public class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

        private let onDismiss: () -> Void
        private let onImagePicked: (UIImage) -> Void

        init(onDismiss: @escaping () -> Void, onImagePicked: @escaping (UIImage) -> Void) {
            self.onDismiss = onDismiss
            self.onImagePicked = onImagePicked
        }

        public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
            
            if let img = info[.editedImage] as? UIImage {
                
                self.onImagePicked(img)
                
            } else if let image = info[.originalImage] as? UIImage {
                self.onImagePicked(image)
            }
            self.onDismiss()
        }

        public func imagePickerControllerDidCancel(_: UIImagePickerController) {
            self.onDismiss()
        }
    }
}

I had tried allows editing which gives me the square to select the photo but I need my given width and height selection of the photo. This is the desired result:

desired

Upvotes: 3

Views: 1698

Answers (1)

user25784919
user25784919

Reputation: 1

struct ImagePicker: UIViewControllerRepresentable {
@Environment(\.presentationMode) private var presentationMode
var sourceType: UIImagePickerController.SourceType = .photoLibrary
@Binding var image: UIImage
var selectedImage: (UIImage, URL, CGSize) -> Void
var isApplyingCrop: Bool = false

func makeUIViewController(context: Context) -> UIImagePickerController {
    let imagePicker = UIImagePickerController()
    imagePicker.allowsEditing = false // Disable built-in editing
    imagePicker.sourceType = sourceType
    imagePicker.delegate = context.coordinator
    return imagePicker
}

func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}

func makeCoordinator() -> Coordinator {
    return Coordinator(self)
}

final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CropViewControllerDelegate {
    var parent: ImagePicker
    var isApplyingCrop: Bool {
        parent.isApplyingCrop
    }

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

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        guard let originalImage = info[.originalImage] as? UIImage else {
            print("No image found")
            parent.presentationMode.wrappedValue.dismiss()
            return
        }

        // Proceed to the cropping controller with the selected image
        let cropViewController = CropViewController(image: originalImage)
        cropViewController.delegate = self
        if isApplyingCrop {
            cropViewController.customAspectRatio = CGSize(width: 270, height: 350)
        }
        picker.present(cropViewController, animated: true, completion: nil)
    }

    // CropViewControllerDelegate methods
    func cropViewController(_ cropViewController: CropViewController, didCropToImage croppedImage: UIImage, withRect cropRect: CGRect, angle: Int) {
        // Generate unique name and URL for the cropped image
        let imageName = UUID().uuidString
        let imagePath = getDocumentsDirectory().appendingPathComponent(imageName).appendingPathExtension("jpg")

        // Save the cropped image
        if let jpegData = croppedImage.jpegData(compressionQuality: 0.8) {
            do {
                try jpegData.write(to: imagePath)
            } catch {
                print("Error saving cropped image: \(error.localizedDescription)")
            }
        }

        // Pass cropped image, URL, and crop size to the closure
        parent.selectedImage(croppedImage, imagePath, cropRect.size)
        parent.image = croppedImage

        // Dismiss both CropViewController and UIImagePickerController
        cropViewController.dismiss(animated: true) {
            self.parent.presentationMode.wrappedValue.dismiss()
        }
    }

    func cropViewControllerDidCancel(_ cropViewController: CropViewController) {
        // Dismiss the CropViewController if the user cancels
        cropViewController.dismiss(animated: true, completion: nil)
    }

    // Helper function to get the documents directory path
    func getDocumentsDirectory() -> URL {
        FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    }
}
}

how to work this

 .fullScreenCover(isPresented: $showImagePicker) {
        if let sourceType = self.sourceType {
            ImagePicker(sourceType: sourceType,
                        image: self.$image,isApplyingCrop:true) { img, url, size  in
                self.isAnyImageGotPicked = true
                self.image = img
                self.filePath = url
            }
            .edgesIgnoringSafeArea(.all)
        }
    }

Upvotes: 0

Related Questions