swiftuicropimage-editingimage-cropper

Best library for image cropping in SwiftUI iOS?


I am working on iOS SwiftUI app Image editor, for image cropping I used the Mantis library but I faced one problem , when I pick image from gallery its working perfectly but getting app crashed when image is picked through camera .Getting this error "Message from debugger: Terminated due to memory issue"

Mantis library install link through package manager

Other library I found is QCropper but it does not have any documentation for SwiftUI

My Code : HomePage

import SwiftUI
import Mantis

struct HomePage: View {
    
    @State var bgImage :UIImage?
    @State var camIsClicked = false
    @State private var isImagePickerDisplay = false
    @State var sourceType: UIImagePickerController.SourceType?
    @State var showCropper = false
    
    @State var gotoImageEdit = false
    @State private var cropShapeType: Mantis.CropShapeType = .rect
    @State private var presetFixedRatioType: Mantis.PresetFixedRatioType = .canUseMultiplePresetFixedRatio()
    var camAlertView:some View{
        VStack {
            VStack{
                VStack(spacing:Constants.device == .pad ? 20:10){
                    Text("Select One!")
                        .foregroundColor(Color.red)
                        .fontWeight(.bold)
                        .font(Constants.device == .pad ? .largeTitle:.title2)
                    Divider()
                        .frame(width:Constants.width*0.65 , height:Constants.device == .pad ? 3.5:2)
                        .background(Color.red)
                }
                .padding(.top)
                Spacer()
                Button {
                    sourceType = .photoLibrary
                    isImagePickerDisplay.toggle()
                } label: {
                    Text("Gallery")
                        .fontWeight(.bold)
                        .font(Constants.device == .pad ? .title:.title3)
                        .frame(width:Constants.width*0.34, height:Constants.device == .pad ? 70:40)
                        .background(Color.red)
                        .cornerRadius(Constants.device == .pad ? 35:20)
                }
                Spacer()
                Button {
                    sourceType = .camera
                    isImagePickerDisplay.toggle()
                } label: {
                    Text("Camera")
                        .fontWeight(.bold)
                        .font(Constants.device == .pad ? .title:.title3)
                        .frame(width:Constants.width*0.34, height:Constants.device == .pad ? 70:40)
                        .background(Color.red)
                        .cornerRadius(Constants.device == .pad ? 35:20)
                    
                }
                Spacer()
            }
            .frame(width:Constants.width*0.65, height:Constants.height*0.39)
            .background(Color.white)
            .cornerRadius(20)
            .foregroundColor(.white)
        }
        .frame(width:Constants.width, height:Constants.height)
        .background(Color.black
                        .opacity(0.8)
                        .ignoresSafeArea()
                        .onTapGesture {
            camIsClicked = false
        })
    }
    var body: some View {
        ZStack{
            NavigationLink( destination: ImageEditPage(bgImage: $bgImage),isActive: $gotoImageEdit) {
                EmptyView()
            }
            VStack{
                Button {
                    camIsClicked.toggle()
                } label: {
                    Text("Pick Image")
                        .padding()
                        .background(Color.green)
                        .cornerRadius(30)
                }
            }
            if camIsClicked{
                camAlertView
            }
            if isImagePickerDisplay{
                if sourceType == .photoLibrary{
                    SUImagePickerView(sourceType: .photoLibrary, image: $bgImage, isPresented: $isImagePickerDisplay, camIsClicked: $camIsClicked, bgImageIsSelected: $showCropper)
                }else{
                    SUImagePickerView(sourceType: .camera, image: $bgImage, isPresented: $isImagePickerDisplay, camIsClicked: $camIsClicked, bgImageIsSelected: $showCropper)
                }
            }
        }
        .navigationBarHidden(true)
        .fullScreenCover(isPresented: $showCropper, content: {
            ImageCropper(gotoImageEdit: $gotoImageEdit, image: $bgImage,cropShapeType: $cropShapeType,presetFixedRatioType: $presetFixedRatioType)
                .ignoresSafeArea()
        })
    }
}

struct HomePage_Previews: PreviewProvider {
    static var previews: some View {
        HomePage()
    }
}

ImageCropper Class

import SwiftUI
import Mantis

struct ImageCropper: UIViewControllerRepresentable {
    @Binding var gotoImageEdit : Bool
    @Binding var image: UIImage?
    @Binding var cropShapeType: Mantis.CropShapeType
    @Binding var presetFixedRatioType: Mantis.PresetFixedRatioType
    @Environment(\.presentationMode) var presentationMode

    class Coordinator: CropViewControllerDelegate {
        func cropViewControllerDidImageTransformed(_ cropViewController: CropViewController) {

        }

        var parent: ImageCropper

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

        func cropViewControllerDidCrop(_ cropViewController: CropViewController, cropped: UIImage, transformation: Transformation, cropInfo: CropInfo) {
            parent.image = cropped
            print("transformation is \(transformation)")
            parent.gotoImageEdit = true
            parent.presentationMode.wrappedValue.dismiss()
        }

        func cropViewControllerDidCancel(_ cropViewController: CropViewController, original: UIImage) {
            parent.presentationMode.wrappedValue.dismiss()
        }

        func cropViewControllerDidFailToCrop(_ cropViewController: CropViewController, original: UIImage) {
        }

        func cropViewControllerDidBeginResize(_ cropViewController: CropViewController) {
        }

        func cropViewControllerDidEndResize(_ cropViewController: CropViewController, original: UIImage, cropInfo: CropInfo) {
        }
    }

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

    func makeUIViewController(context: Context) -> CropViewController {
        var config = Mantis.Config()
        config.cropViewConfig.cropShapeType = cropShapeType
        config.presetFixedRatioType = presetFixedRatioType
        let cropViewController = Mantis.cropViewController(image: image!,
                                                           config: config)
        cropViewController.delegate = context.coordinator
        return cropViewController
    }

    func updateUIViewController(_ uiViewController: CropViewController, context: Context) {

    }
}

Solution

  • I think it is a bug in the last version of the mantis library. I had the same problem. I installed version 2.1.2 and it seems to work. Your code looks fine.