99#import < Photos/Photos.h>
1010#import < UIKit/UIKit.h>
1111
12+ #import " FLTImagePickerController.h"
1213#import " FLTImagePickerImageUtil.h"
1314#import " FLTImagePickerMetaDataUtil.h"
1415#import " FLTImagePickerPhotoAssetUtil.h"
1516
16- @interface FLTImagePickerPlugin () <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
17+ @interface FLTImagePickerPlugin ()
1718
1819@property (copy , nonatomic ) FlutterResult result;
1920
@@ -24,7 +25,7 @@ @interface FLTImagePickerPlugin () <UINavigationControllerDelegate, UIImagePicke
2425
2526@implementation FLTImagePickerPlugin {
2627 NSDictionary *_arguments;
27- UIImagePickerController *_imagePickerController;
28+ FLTImagePickerController *_imagePickerController;
2829 UIImagePickerControllerCameraDevice _device;
2930}
3031
@@ -64,12 +65,12 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
6465 message: @" Cancelled by a second request"
6566 details: nil ]);
6667 self.result = nil ;
68+ return ;
6769 }
6870
71+ _imagePickerController = [[FLTImagePickerController alloc ] initWithPlugin: self ];
72+
6973 if ([@" pickImage" isEqualToString: call.method]) {
70- _imagePickerController = [[UIImagePickerController alloc ] init ];
71- _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
72- _imagePickerController.delegate = self;
7374 _imagePickerController.mediaTypes = @[ (NSString *)kUTTypeImage ];
7475
7576 self.result = result;
@@ -95,9 +96,6 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
9596 break ;
9697 }
9798 } else if ([@" pickVideo" isEqualToString: call.method]) {
98- _imagePickerController = [[UIImagePickerController alloc ] init ];
99- _imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
100- _imagePickerController.delegate = self;
10199 _imagePickerController.mediaTypes = @[
102100 (NSString *)kUTTypeMovie , (NSString *)kUTTypeAVIMovie , (NSString *)kUTTypeVideo ,
103101 (NSString *)kUTTypeMPEG4
@@ -142,9 +140,7 @@ - (void)showCamera {
142140 [UIImagePickerController isCameraDeviceAvailable: _device]) {
143141 _imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
144142 _imagePickerController.cameraDevice = _device;
145- [[self viewControllerWithWindow: nil ] presentViewController: _imagePickerController
146- animated: YES
147- completion: nil ];
143+ [self PresentImagePickerController ];
148144 } else {
149145 [[[UIAlertView alloc ] initWithTitle: @" Error"
150146 message: @" Camera not available."
@@ -249,20 +245,20 @@ - (void)errorNoPhotoAccess:(PHAuthorizationStatus)status {
249245- (void )showPhotoLibrary {
250246 // No need to check if SourceType is available. It always is.
251247 _imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
248+ [self PresentImagePickerController ];
249+ }
250+
251+ - (void )PresentImagePickerController {
252252 [[self viewControllerWithWindow: nil ] presentViewController: _imagePickerController
253253 animated: YES
254254 completion: nil ];
255255}
256256
257- - (void )imagePickerController : (UIImagePickerController *) picker
258- didFinishPickingMediaWithInfo : ( NSDictionary <NSString *, id> *) info {
257+ - (void )FinishPickingMediaWithInfo : ( NSDictionary <NSString *, id> *_Nonnull) info
258+ completion : ( void (^)( void )) completion {
259259 NSURL *videoURL = [info objectForKey: UIImagePickerControllerMediaURL];
260- [_imagePickerController dismissViewControllerAnimated: YES completion: nil ];
261- // The method dismissViewControllerAnimated does not immediately prevent
262- // further didFinishPickingMediaWithInfo invocations. A nil check is necessary
263- // to prevent below code to be unwantly executed multiple times and cause a
264- // crash.
265260 if (!self.result ) {
261+ completion ();
266262 return ;
267263 }
268264 if (videoURL != nil ) {
@@ -281,6 +277,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker
281277 message: @" Could not cache the video file."
282278 details: nil ]);
283279 self.result = nil ;
280+ completion ();
284281 return ;
285282 }
286283 }
@@ -290,6 +287,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker
290287 self.result (videoURL.path );
291288 self.result = nil ;
292289 _arguments = nil ;
290+ completion ();
293291 } else {
294292 UIImage *image = [info objectForKey: UIImagePickerControllerEditedImage];
295293 if (image == nil ) {
@@ -316,6 +314,7 @@ - (void)imagePickerController:(UIImagePickerController *)picker
316314 if (!originalAsset) {
317315 // Image picked without an original asset (e.g. User took a photo directly)
318316 [self saveImageWithPickerInfo: info image: image imageQuality: imageQuality];
317+ completion ();
319318 } else {
320319 __weak typeof (self) weakSelf = self;
321320 [[PHImageManager defaultManager ]
@@ -329,19 +328,43 @@ - (void)imagePickerController:(UIImagePickerController *)picker
329328 maxWidth: maxWidth
330329 maxHeight: maxHeight
331330 imageQuality: imageQuality];
331+ completion ();
332332 }];
333333 }
334334 }
335335}
336336
337+ - (void )imagePickerController : (UIImagePickerController *)picker
338+ didFinishPickingMediaWithInfo : (NSDictionary <NSString *, id> *)info {
339+ [self FinishPickingMediaWithInfo: info
340+ completion: ^{
341+ dispatch_async (dispatch_get_main_queue (), ^{
342+ // Dismissing the image picker before cleaning up leads to a race
343+ // condition, where the result is not cleaned before the next image
344+ // picker is brought up. So we dismiss the `_imagePickerController` as
345+ // the last step.
346+ [self ->_imagePickerController dismissViewControllerAnimated: YES
347+ completion: nil ];
348+ });
349+ }];
350+ }
351+
337352- (void )imagePickerControllerDidCancel : (UIImagePickerController *)picker {
338- [_imagePickerController dismissViewControllerAnimated: YES completion: nil ];
353+ [self handleImagePickerControllerDismissed ];
354+ }
355+
356+ - (void )handleImagePickerControllerDismissed {
339357 if (!self.result ) {
358+ [_imagePickerController dismissViewControllerAnimated: YES completion: nil ];
340359 return ;
341360 }
342361 self.result (nil );
343362 self.result = nil ;
344363 _arguments = nil ;
364+ // Dismissing the image picker before cleaning up leads to a race condition,
365+ // where the result is not cleaned before the next image picker is brought up.
366+ // So we dismiss the `_imagePickerController` as the last step.
367+ [_imagePickerController dismissViewControllerAnimated: YES completion: nil ];
345368}
346369
347370- (void )saveImageWithOriginalImageData : (NSData *)originalImageData
0 commit comments