diff --git a/packages/image_picker/image_picker_ios/CHANGELOG.md b/packages/image_picker/image_picker_ios/CHANGELOG.md index a8b8c31a14f..95d4a5bb558 100644 --- a/packages/image_picker/image_picker_ios/CHANGELOG.md +++ b/packages/image_picker/image_picker_ios/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.8.13+4 + +* Fixes camera confirmation buttons (e.g., Retake/Use Photo) taps passing through to the underlying Flutter UI while the picker is dismissing on some iOS versions (e.g., iOS 26). + ## 0.8.13+3 * Fixes a performance regression on iOS where picking videos could cause a long delay due to transcoding. The picker is now configured to request the original asset to avoid conversion. diff --git a/packages/image_picker/image_picker_ios/example/lib/main.dart b/packages/image_picker/image_picker_ios/example/lib/main.dart index 28fc2875266..cbd2821bfb8 100755 --- a/packages/image_picker/image_picker_ios/example/lib/main.dart +++ b/packages/image_picker/image_picker_ios/example/lib/main.dart @@ -522,7 +522,9 @@ class _MyHomePageState extends State { ? int.parse(limitController.text) : null; onPick(width, height, quality, limit); - Navigator.of(context).pop(); + // Leave the dialog open to verify that tapping the transparent area no longer pops it. + // Regression check for https://github.com/flutter/flutter/issues/173453. + // Navigator.of(context).pop(); }, ), ], diff --git a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m index 8ca93e706a9..1d49edad3bf 100644 --- a/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m @@ -37,6 +37,8 @@ @interface FLTImagePickerPlugin () /// the array. @property(strong, nonatomic) NSMutableArray *imagePickerControllerOverrides; +@property(strong, nonatomic) UIWindow *interactionBlockerWindow; +@property(weak, nonatomic) UIWindow *previousKeyWindow; @end @@ -323,6 +325,7 @@ - (void)showCamera:(UIImagePickerControllerCameraDevice)device [UIImagePickerController isCameraDeviceAvailable:device]) { imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; imagePickerController.cameraDevice = device; + [self addInteractionBlocker]; [[self viewControllerWithWindow:nil] presentViewController:imagePickerController animated:YES completion:nil]; @@ -532,7 +535,11 @@ - (void)picker:(PHPickerViewController *)picker - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { NSURL *videoURL = info[UIImagePickerControllerMediaURL]; - [picker dismissViewControllerAnimated:YES completion:nil]; + __weak typeof(self) weakSelf = self; + [picker dismissViewControllerAnimated:YES + completion:^{ + [weakSelf removeInteractionBlocker]; + }]; // The method dismissViewControllerAnimated does not immediately prevent // further didFinishPickingMediaWithInfo invocations. A nil check is necessary // to prevent below code to be unwantly executed multiple times and cause a @@ -618,7 +625,11 @@ - (void)imagePickerController:(UIImagePickerController *)picker } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { - [picker dismissViewControllerAnimated:YES completion:nil]; + __weak typeof(self) weakSelf = self; + [picker dismissViewControllerAnimated:YES + completion:^{ + [weakSelf removeInteractionBlocker]; + }]; [self sendCallResultWithSavedPathList:nil]; } @@ -674,4 +685,44 @@ - (void)sendCallResultWithError:(FlutterError *)error { self.callContext = nil; } +- (void)addInteractionBlocker { + if (self.interactionBlockerWindow != nil) { + return; + } + UIViewController *topController = [self viewControllerWithWindow:nil]; + UIWindow *presentingWindow = topController.view.window; + if (!presentingWindow) { + return; + } + self.previousKeyWindow = presentingWindow; + UIWindow *blockerWindow; + if (@available(iOS 13.0, *) && presentingWindow.windowScene) { + blockerWindow = [[UIWindow alloc] initWithWindowScene:presentingWindow.windowScene]; + } else { + blockerWindow = [[UIWindow alloc] initWithFrame:presentingWindow.bounds]; + } + blockerWindow.frame = presentingWindow.bounds; + blockerWindow.autoresizingMask = + UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + blockerWindow.windowLevel = presentingWindow.windowLevel + 1; + UIViewController *vc = [[UIViewController alloc] init]; + vc.view.backgroundColor = [UIColor clearColor]; + vc.view.userInteractionEnabled = YES; + blockerWindow.rootViewController = vc; + [blockerWindow makeKeyAndVisible]; + self.interactionBlockerWindow = blockerWindow; +} + +- (void)removeInteractionBlocker { + if (!self.interactionBlockerWindow) { + return; + } + self.interactionBlockerWindow.hidden = YES; + if (self.previousKeyWindow) { + [self.previousKeyWindow makeKeyWindow]; + } + self.interactionBlockerWindow = nil; + self.previousKeyWindow = nil; +} + @end diff --git a/packages/image_picker/image_picker_ios/pubspec.yaml b/packages/image_picker/image_picker_ios/pubspec.yaml index 30b0194fd16..90791363595 100755 --- a/packages/image_picker/image_picker_ios/pubspec.yaml +++ b/packages/image_picker/image_picker_ios/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker_ios description: iOS implementation of the image_picker plugin. repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_ios issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22 -version: 0.8.13+3 +version: 0.8.13+4 environment: sdk: ^3.9.0