ivpusic / react-native-image-crop-picker

iOS/Android image picker with support for camera, video, configurable compression, multiple images and cropping
MIT License
6.09k stars 1.55k forks source link

No "cancel cropper" error callback #1540

Open watadarkstar opened 3 years ago

watadarkstar commented 3 years ago

Version

Tell us which versions you are using:

Platform

Tell us to which platform this issue is related

Expected behaviour

Error is reported when cropper is cancelled E_CROPPER_CANCELLED

Actual behaviour

No error is reported and the await never resolves.

Steps to reproduce

  1. Take a photo on iOS

  2. Press "Cancel" on the cropper screen

  3. The error callback nor the then callback is resolved.

Attachments

Image from iOS (18)

socialcode-rob1 commented 3 years ago

It provides an error callback when attempting to cancel. You need to catch the error and look for the cancel code and handle accordingly.

example

    .catch((err) => {
      if (err.code.match(/E_PICKER_CANCELLED/)) {
        cancelHandler();
      } else {
        errorHandler();
      }
    });

you can optionally bring in the list of codes as well

draperunner commented 3 years ago

@socialcode-rob1 No, the error callback is not called. I have the same problem. The openCamera promise never resolves.

In my case, I can make work my way around this by displaying a cancel button on the view underneath, which the user can press to reset the state. This works because the cropper view actually disappears, although the callback is not called.

rusty428 commented 3 years ago

"react-native-image-crop-picker": "0.36.2"

I can confirm that the error callback is not called when cancelling the cropper from both openCamera and openPicker in iOS.

When using openPicker, cancelling the cropper results in the library being displayed. Then cancelling the library does fire the error callback, so we eventually get there. But... from the cropper itself, no error callback.

The openCamera does not have that fallback point. The cancel button on the cropper closes both the cropper and the openCamera, but with no callback.

The error callback does appear to be called when cancelling the cropper in Android.

My workaround is to disable the cropper on both openCamera and openPicker.

cropping: false,

I then call the image cropper after the image is captured or selected:

function captureImage(thisImageURI) { ImagePicker.openCamera({ width: 2000, height: 2000, cropping: false, mediaType: 'photo', }).then(image => { //console.log('camera response: '+JSON.stringify(image)); cropImage(image.path) }).catch((err) => { console.log('openCamera Error: '+err.message) //console.log('openCamera Error: '+JSON.stringify(err)) }); }

function cropImage(thisImageURI) { ImagePicker.openCropper({ path: thisImageURI, width: 2000, height: 2000, includeBase64: true, avoidEmptySpaceAroundImage: false, freeStyleCropEnabled: false, }) .then((image) => { console.log('received cropped image', image); }) .catch((e) => { console.log(e); }); }

There is a "flash" between the image selection and the cropper loading, but with some creative styling I was able to make that fairly unobtrusive.

This "works"... but is a pretty heavy workaround

LucasHimelfarb commented 2 years ago

dont use "then and catch", u can make a clean code as follows:

const sampleFunction = async () => {
    try {
       const options = { width: 400, height: 400, cropped: true };
       const result = await ImagePicker.openCamera(options);

       console.log(result);
    }
    catch(error) {
       console.log(error)
    }
};

u can see "cancel by user" in catch

dinhbt commented 2 years ago

You can update code in ImageCropPicker.m

#define ERROR_CROP_CANCEL_KEY @"E_CROP_CANCELLED"
#define ERROR_CROP_CANCEL_MSG @"User cancelled image crop"

- (void)dismissCropper:(UIViewController *)controller selectionDone:(BOOL)selectionDone completion:(void (^)(void))completion {
    switch (self.currentSelectionMode) {
        case CROPPING:
            [controller dismissViewControllerAnimated:YES completion:completion];
            break;
        case PICKER:
            if (selectionDone) {
                [controller.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:completion];
            } else {
                // if user opened picker, tried to crop image, and cancelled cropping
                // return him to the image selection instead of returning him to the app
                [controller.presentingViewController dismissViewControllerAnimated:YES completion:completion];
            }
            break;
        case CAMERA:
            if (selectionDone) {
                [controller.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:completion];
            } else {
                // if user opened camera capture => tried to crop image, and cancelled cropping
                [controller dismissViewControllerAnimated:YES completion:[self waitAnimationEnd:^{
                    self.reject(ERROR_CROP_CANCEL_KEY, ERROR_CROP_CANCEL_MSG, nil);
                }]];
            }
            break;
    }
}
            }
            break;

and use try catch

const sampleFunction = async () => {
    try {
       const options = { width: 400, height: 400, cropped: true };
       const result = await ImagePicker.openCamera(options);

       console.log(result);
    }
    catch(error) {
      // E_CROP_CANCELLED
       console.log(error)
    }
};
draperunner commented 2 years ago

Great find! Maybe you could submit a PR on this, @dinhbt?

And a markdown tip: Use diff to mark lines changed more easily, like so:

This line has not been changed
+This line has been added and is green
-This line is removed

Just write diff after the first three backticks and use + and -:

```diff
This line has not been changed
+This line has been added and is green
-This line is removed
mahshid22 commented 1 year ago

try, catch works for me!