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

Conversation

chris-rutkowski
Copy link
Contributor

@chris-rutkowski chris-rutkowski commented May 13, 2020

Description

Existing implementation is incorrect especially when adding project as a module with more complex presentation structure.
If window's root view controller has presented modally any other view controller, it cannot present any other any more. Only the top most controller can present other.

With this change, the image picker/camera controller is presented on the top most view controller.

Related Issues

I found this issue raised by someone already:
flutter/flutter#34787

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • My PR includes unit or integration tests for all changed/updated/fixed behaviors (See Contributor Guide).
  • All existing and new tests are passing.
  • I updated/added relevant documentation (doc comments with ///).
  • The analyzer (flutter analyze) does not report any problems on my PR.
  • I read and followed the Flutter Style Guide.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I signed the CLA.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Does your PR require plugin users to manually update their apps to accommodate your change?

  • Yes, this is a breaking change (please indicate a breaking change in CHANGELOG.md and increment major revision).
  • No, this is not a breaking change.

…image or camera picker.

Existing implementation is incorrect especially when adding project as a module with more complex presentation structure.
If window's root view controller has presented modally any other view controller, it cannot present any other any more. Only the top most controller can present other.

With this change, the image picker/camera controller is presented on the top most view controller.
@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment @googlebot I fixed it.. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ Googlers: Go here for more info.

@chris-rutkowski chris-rutkowski force-pushed the image_picker_dynamic_top_view_controller branch from d59a83b to 9f58745 Compare May 13, 2020 03:27
@googlebot
Copy link

CLAs look good, thanks!

ℹ️ Googlers: Go here for more info.

@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment @googlebot I fixed it.. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ Googlers: Go here for more info.

@chris-rutkowski chris-rutkowski force-pushed the image_picker_dynamic_top_view_controller branch from cc95b7d to d75a864 Compare May 13, 2020 06:35
@googlebot
Copy link

CLAs look good, thanks!

ℹ️ Googlers: Go here for more info.

jlalvarez18 pushed a commit to jlalvarez18/plugins that referenced this pull request May 14, 2020
Contains code from the following PRs:

flutter#2761
flutter#2755
@chris-rutkowski
Copy link
Contributor Author

@xster @mehmetf @cyanglaz @blasten I saw you merged some other PRs related to image picker recently, that's why I'm @-ing you. Is there anything more I can do here in this PR?

Copy link
Contributor

@cyanglaz cyanglaz left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! It looks good overall. I left some comments.

- (UIImagePickerController *)getImagePickerController {
return _imagePickerController;
}

- (UIViewController *)viewController {
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
Copy link
Contributor

Choose a reason for hiding this comment

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

this is great! By the way, while we are here, can we also make it work for navigation controller? (pushed viewcontrollers)

Copy link
Contributor

Choose a reason for hiding this comment

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

also tab bar controller if possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this solution will work in this case as well.
According to the documentation you shouldn't push UIImagePickerController, but present it modally instead.

So in the situation that FlutterViewController is pushed into an existing navigation controller, e.g.:

Window
  rootViewController
    someOtherViewController
      navigation controller (with some native view controller and FlutterVC)
         image picker controller 

not

Window
  rootViewController
    someOtherViewController
      navigation controller (with some native view controller and FlutterVC, image picker controller)
          

I was worried about iPad which for a long time required popover presentation for albums, but since iOS 13 it presents modally as well without an issue.

Copy link
Contributor

Choose a reason for hiding this comment

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

what i meant was that if topViewController is a view controller that is pushed in the navigation stack.
And we want to present the image picker controller on top of that.

Copy link
Contributor Author

@chris-rutkowski chris-rutkowski May 29, 2020

Choose a reason for hiding this comment

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

@cyanglaz the solution I made will work in virtually all cases, including these you are concerned about.

UINavigationController, UITabBarController they both manage internally some view controllers, but they themselves inherit from UIViewControllerr and either of them can be window's root view controller, or presented on some existing View Controller of any type.

The topViewController cannot be pushed in the navigation stack. In that case, UINavigationCoontroller is topViewController and some other push view controller is managed by UINavigationController. And with that arrangement, my solution will correctly display the Image Picker on top of the navigation controller.

Trust me - in my company we have already very complex navigation - some legacy Drawer Controller (in iOS), multiple Navigation controllers, TabBarControllers and Flutter V.C. is displayed on top of all of this. And my solution has been proven successful.

Copy link
Contributor

Choose a reason for hiding this comment

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

according to https://developer.apple.com/documentation/uikit/uiviewcontroller/1621407-presentedviewcontroller?language=objc the presentedViewController is only the top view controller when it is presented with presentViewController:animated:completion:.
What if the top view controller is presented by pushViewController? Does this still work?

Copy link
Contributor Author

@chris-rutkowski chris-rutkowski Jun 11, 2020

Choose a reason for hiding this comment

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

@cyanglaz I am already repeating myself, but let me rephrase.

presentViewController is a method of UIViewController
UINavigationController, UITabBarController, MySweetViewController - they are all subclasses of UIViewController, therefore they can be presented on any other view controller, or some other view controller can be presented on top of them.

The top view controller presented by push view controller - such a thing doesn't exist.
You can have UINavigationController either as a root of the window, or presented on some other view controller. And Navigation Controller manages the stack of view controllers. These view controllers shouldn't present any other view controller (because they are managed by NC). Instead, you either push another VC in NC (horizontal), or present VC on top of NC (vertical).

Moreover - if UINavigationController is one of the children of UITabBarController, you shouldn't present any VC on top of that NC, you should present on top of UITabBarController instead.

Some diagram

example1:

window
  view controller 1
     view controller 2
        image picker

example2:
window
  navigation controller (home, details)
     image picker

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok. I'm interested in this case you presented

example2:
window
  navigation controller (home, details)
     image picker

In this case, when you go through the code, what is the top view controller? Is it the navigation controller?
And the image picker is going to use the navigation controller as the "viewController", regardless which view controller is the on the top of the stack of the navigation controller? Am I understanding correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

exactly, image picker will be correctly presented on navigation controller. The contents of navigation controller doesn't matter at all.

@chris-rutkowski
Copy link
Contributor Author

@cyanglaz as requested changed version to 0.6.6+5

@chris-rutkowski
Copy link
Contributor Author

@cyanglaz any more feedback? I've already updated changelog a few times due to conflicts - other PRs being merged.

@aboutlan
Copy link

aboutlan commented Jun 1, 2020

@cyanglaz I have the same problem.
I presented flutterViewController after I use present image_picker doesn't work, Xcode error warning: Attempt to present <UIImagePickerController: 0x7fbbb1856800> on <UINavigationController: 0x7fbbb201c000> whose view is not in the window hierarchy !
@chris-rutkowski PR solved my problem.

@diegotori
Copy link

@cyanglaz a gentle reminder for reviewing this PR, since this could be very useful to have.

@chris-rutkowski looks like your branch has conflicts that need to be resolved before this can merge. Thank you in advance for this PR.

Copy link
Contributor

@cyanglaz cyanglaz left a comment

Choose a reason for hiding this comment

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

LGTM! Can we add tests for this?

window in normal circumstances is nil - in that case the key window will be found
adds unit tests
@chris-rutkowski
Copy link
Contributor Author

@cyanglaz added unit test and improved the solution a bit.

BTW I cannot find any guide to run iOS tests. I had to create a dummy project which wasn't very easy.

@cyanglaz
Copy link
Contributor

@chris-rutkowski
try

cd ios
pod lib lint

@@ -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.

@cyanglaz cyanglaz merged commit ff53b70 into flutter:master Jun 15, 2020
@chris-rutkowski chris-rutkowski deleted the image_picker_dynamic_top_view_controller branch June 17, 2020 06:30
@chris-rutkowski chris-rutkowski restored the image_picker_dynamic_top_view_controller branch June 17, 2020 06:30
agent3bood pushed a commit to agent3bood/flutter-plugins that referenced this pull request Jul 10, 2020
jorgefspereira pushed a commit to jorgefspereira/plugins_flutter that referenced this pull request Oct 10, 2020
FlutterSu pushed a commit to FlutterSu/flutter-plugins that referenced this pull request Nov 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants