Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[image_picker] fixes for iOS which doesn't present camera/albums with more complex navigation #2755

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ Giancarlo Rocha <[email protected]>
Ryo Miyake <[email protected]>
Théo Champion <[email protected]>
Kazuki Yamaguchi <[email protected]>
Eitan Schwartz <[email protected]>
Eitan Schwartz <[email protected]>
Chris Rutkowski <[email protected]>
4 changes: 4 additions & 0 deletions packages/image_picker/image_picker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.7+2

* iOS: Fixes unpresentable album/image picker if window's root view controller is already presenting other view controller.

## 0.6.7+1

* Add web support to the example app.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
@interface FLTImagePickerPlugin : NSObject <FlutterPlugin>

// For testing only.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove this line now?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so? Both of these methods here are exposed only for testing and not meant to be used in normal circumstances.

- (instancetype)initWithViewController:(UIViewController *)viewController;
- (UIImagePickerController *)getImagePickerController;
- (UIViewController *)viewControllerWithWindow:(UIWindow *)window;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,39 @@ @interface FLTImagePickerPlugin () <UINavigationControllerDelegate, UIImagePicke
@implementation FLTImagePickerPlugin {
NSDictionary *_arguments;
UIImagePickerController *_imagePickerController;
UIViewController *_viewController;
UIImagePickerControllerCameraDevice _device;
}

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
FlutterMethodChannel *channel =
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/image_picker"
binaryMessenger:[registrar messenger]];
UIViewController *viewController =
[UIApplication sharedApplication].delegate.window.rootViewController;
FLTImagePickerPlugin *instance =
[[FLTImagePickerPlugin alloc] initWithViewController:viewController];
FLTImagePickerPlugin *instance = [FLTImagePickerPlugin new];
[registrar addMethodCallDelegate:instance channel:channel];
}

- (instancetype)initWithViewController:(UIViewController *)viewController {
self = [super init];
if (self) {
_viewController = viewController;
}
return self;
}

- (UIImagePickerController *)getImagePickerController {
return _imagePickerController;
}

- (UIViewController *)viewControllerWithWindow:(UIWindow *)window {
UIWindow *windowToUse = window;
if (windowToUse == nil) {
for (UIWindow *window in [UIApplication sharedApplication].windows) {
if (window.isKeyWindow) {
windowToUse = window;
break;
}
}
}

UIViewController *topController = windowToUse.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
return topController;
}

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
if (self.result) {
self.result([FlutterError errorWithCode:@"multiple_request"
Expand Down Expand Up @@ -136,7 +142,9 @@ - (void)showCamera {
[UIImagePickerController isCameraDeviceAvailable:_device]) {
_imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
_imagePickerController.cameraDevice = _device;
[_viewController presentViewController:_imagePickerController animated:YES completion:nil];
[[self viewControllerWithWindow:nil] presentViewController:_imagePickerController
animated:YES
completion:nil];
} else {
[[[UIAlertView alloc] initWithTitle:@"Error"
message:@"Camera not available."
Expand Down Expand Up @@ -241,7 +249,9 @@ - (void)errorNoPhotoAccess:(PHAuthorizationStatus)status {
- (void)showPhotoLibrary {
// No need to check if SourceType is available. It always is.
_imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[_viewController presentViewController:_imagePickerController animated:YES completion:nil];
[[self viewControllerWithWindow:nil] presentViewController:_imagePickerController
animated:YES
completion:nil];
}

- (void)imagePickerController:(UIImagePickerController *)picker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@
@import image_picker;
@import XCTest;

@interface MockViewController : UIViewController
@property(nonatomic, retain) UIViewController *mockPresented;
@end

@implementation MockViewController
@synthesize mockPresented;

- (UIViewController *)presentedViewController {
return mockPresented;
}

@end

@interface FLTImagePickerPlugin (Test)
@property(copy, nonatomic) FlutterResult result;
- (void)handleSavedPath:(NSString *)path;
Expand All @@ -23,8 +36,7 @@ - (void)testPluginPickImageDeviceBack {
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
return;
}
FLTImagePickerPlugin *plugin =
[[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]];
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickImage"
arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}];
Expand All @@ -39,8 +51,7 @@ - (void)testPluginPickImageDeviceFront {
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
return;
}
FLTImagePickerPlugin *plugin =
[[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]];
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickImage"
arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
Expand All @@ -55,8 +66,7 @@ - (void)testPluginPickVideoDeviceBack {
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
return;
}
FLTImagePickerPlugin *plugin =
[[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]];
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickVideo"
arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}];
Expand All @@ -71,8 +81,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
return;
}
FLTImagePickerPlugin *plugin =
[[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]];
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickImage"
arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
Expand All @@ -90,8 +99,7 @@ - (void)testPluginPickVideoDeviceFront {
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
return;
}
FLTImagePickerPlugin *plugin =
[[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]];
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickVideo"
arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}];
Expand All @@ -104,8 +112,7 @@ - (void)testPluginPickVideoDeviceFront {

#pragma mark - Test video duration
- (void)testPickingVideoWithDuration {
FLTImagePickerPlugin *plugin =
[[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]];
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call = [FlutterMethodCall
methodCallWithMethodName:@"pickVideo"
arguments:@{@"source" : @(0), @"cameraDevice" : @(0), @"maxDuration" : @95}];
Expand All @@ -116,8 +123,7 @@ - (void)testPickingVideoWithDuration {
}

- (void)testPluginPickImageSelectMultipleTimes {
FLTImagePickerPlugin *plugin =
[[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]];
FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
FlutterMethodCall *call =
[FlutterMethodCall methodCallWithMethodName:@"pickImage"
arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}];
Expand All @@ -131,4 +137,16 @@ - (void)testPluginPickImageSelectMultipleTimes {
[plugin handleSavedPath:@"test"];
}

- (void)testViewController {
UIWindow *window = [UIWindow new];
MockViewController *vc1 = [MockViewController new];
window.rootViewController = vc1;

UIViewController *vc2 = [UIViewController new];
vc1.mockPresented = vc2;

FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new];
XCTAssertEqual([plugin viewControllerWithWindow:window], vc2);
}

@end
2 changes: 1 addition & 1 deletion packages/image_picker/image_picker/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: image_picker
description: Flutter plugin for selecting images from the Android and iOS image
library, and taking new pictures with the camera.
homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker
version: 0.6.7+1
version: 0.6.7+2

flutter:
plugin:
Expand Down