0

I'm trying to detect when the imagePickerController via swipe is closed via swipe-down gesture or via cancel.

The imagePicker is loaded via this method (https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-presentviewcontroller)

[rootViewController presentViewController:picker animated:animated completion:NULL];

and we can simply detect if the pickerController is closed via cancel by implementing this method (https://developer.apple.com/documentation/uikit/uiimagepickercontrollerdelegate/1619133-imagepickercontrollerdidcancel)

however, I also want to detect if it is closed via swipe down (for iPhone X, ..., we can swipe down to close a view that is show modally)

with Swift, I can detect it with this code:

extension UIImagePickerController {
    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        // detecting
    }
}

I wonder if the is an equivalent way to do this in objective C (since the project I'm working on is written in objective C)? or any other suggestion is welcomed :D

vudangkhoa2906
  • 143
  • 1
  • 8
  • 1
    Note that overriding in extensions is not supported: "Extensions can add new functionality to a type, but they cannot override existing functionality". You should not rely on the fact that this works right now. Implement a real subclass. – Gereon Mar 23 '20 at 08:19

1 Answers1

7

First of all, you should not override viewDidDisappear: in an extension in Swift (nor in a category in Objective-C). The result of overriding something in an extension/category is undefined behavior - it may work, it may not. It should never be relied upon.

Instead, assign the delegate of your image picker's presentationController to some class, then have that class implement the presentationControllerDidDismiss: method. This method is called when the user has taken action to dismiss the image picker successfully, after all animations are finished. (And note that it's not called if the image picker is dismissed programatically.)

Here's a short example that covers all the cases of dismissing your image picker without the need to override viewDidDisappear: in an extension or category:

@interface ViewController() <UIAdaptivePresentationControllerDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate>

@end

@implementation ViewController

- (IBAction)showImagePicker {
    UIImagePickerController *imagePicker = [UIImagePickerController new];
    imagePicker.delegate = self;
    imagePicker.presentationController.delegate = self;
    [self presentViewController:imagePicker animated:YES completion:nil];
}

#pragma mark - UIAdaptivePresentationControllerDelegate

- (void)presentationControllerWillDismiss:(UIPresentationController *)presentationController {
    NSLog(@"The user began to swipe down to dismiss.");
}

- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController {
    NSLog(@"The dismissal animation finished after the user swiped down.");

    // This is probably where you want to put your code that you want to call.
}

#pragma mark - UIImagePickerControllerDelegate, UINavigationControllerDelegate

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    NSLog(@"The user tapped the image picker's Cancel button.");
    [self dismissViewControllerAnimated:YES completion:^{
        NSLog(@"The dismissal animation finished after the user tapped Cancel.");
    }];
}

- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey, id> *)info {
    NSLog(@"The user selected an image from the image picker.");
    [self dismissViewControllerAnimated:YES completion:^{
        NSLog(@"The dismissal animation finished after the user selected an image.");
    }];
}

@end

And here's a Swift version for good measure:

class ViewController: UIViewController {
    @IBAction func showImagePicker() {
        let imagePicker = UIImagePickerController()
        imagePicker.delegate = self
        imagePicker.presentationController?.delegate = self
        present(imagePicker, animated: true)
    }
}

extension ViewController: UIAdaptivePresentationControllerDelegate {
    func presentationControllerWillDismiss(_ presentationController: UIPresentationController) {
        print("The user began to swipe down to dismiss.")
    }

    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        print("The dismissal animation finished after the user swiped down.")

        // This is probably where you want to put your code that you want to call.
    }
}

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        print("The user tapped the image picker's Cancel button.")
        dismiss(animated: true) {
            print("The dismissal animation finished after the user tapped Cancel.")
        }
    }

    func imagePickerController(_ picker: UIImagePickerController,
                               didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        print("The user selected an image from the image picker.")
        dismiss(animated: true){
            print("The dismissal animation finished after the user selected an image.")
        }
    }
}
TylerP
  • 9,600
  • 4
  • 39
  • 43
  • 1
    hi Tyler Thank you for your answer. I understand the idea behind it now. However, I did exactly as you suggested: - step 1: let my view controller conform to UIAdaptivePresentationControllerDelegate - step 2: implement the method presentationControllerDidDismiss - step 3: assign imagePicker.presentationController.delegate to my view controller but when the view is swiped down, the method is not called. Are you sure presentationControllerDidDismiss is indeed invoked when the view is swiped out? – vudangkhoa2906 Mar 23 '20 at 09:44
  • Hmmm, I'm not really sure why it's not working for you (it does for me). If you're using a popover modal presentation style then take a look at [this question](https://stackoverflow.com/questions/59149126/ios-13s-presentationcontrollerdiddismiss-not-called-for-popover-in-compact-en) which has another helpful answer from @matt. Otherwise, maybe make a new question that shows your attempt to use the above code so we can see what's wrong. – TylerP Mar 23 '20 at 16:25