-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Using the image_picker plugin, selecting and resizing a portrait-oriented photo on iOS 14 will cause incorrect rotation to be applied - but only if the app has "Selected Photos" (limited) permission.
Background
iOS 14 introduces a new, limited permission for accessing photos from the gallery. If chosen, the app can only access photos selected by user, and most importantly to this bug, attempting to get a PHAsset will fail (as a PHAsset may include more metadata than what iOS wants to give you).
Why is this error occurring
We can step through the typical image picking flow to understand what is happening:
- Flutter app requests an image from the gallery, and asks for it to be resized (by setting
maxWidthormaxHeight - The plugin checks or requests photos permissions. The plugin uses the older, simpler, deprecated permissions request, and therefore does not explicitly handle limited access. However, that option is presented to the user, and if they choose "Select Photos..." we will see the problem.
- An image is chosen by the user
- As we have set a max dimension, the plugin will scale the image. The implementation here is that the scale occurs with a hard-coded orientation, assuming that the exif rotation data will be added later. This is where the issue originates
- Once the image has been resized, the plugin will attempt to add all the original metadata (including orientation flag) to the newly created, scaled, image. It does this by accessing a
PHAssetof the original image. PHAsset will benilbecause of the limited photo permission - Because the asset returned is
nil, the image is saved without adding any of the original metadata. Without the original metadata, the scaling operation from Step 4 means that the image is now saved rotated incorrectly
Why is it only occuring if we are scaling?
The plugin uses the original image returned from the UIImagePicker if we aren't scaling. This image has the correct orientation flag, and so the image keeps the correct orientation.
Why is it only occuring if the app has limited access?
The Apple docs are fairly non-existent here, but access to PHAsset isn't allowed if the app only has limited photo access. However, with full access granted, the PHAsset is successfully used to copy the metadata (including orientation flag), and so the resized image has all the correct metadata.
What about other metadata?
Good pickup - The real title of this bug report should be "scaled image is missing all metadata if chosen from Gallery on iOS14 with limited photo permission". However, incorrect image rotation is the most visible symptom and is most likely to be the problem other users find.
Possible Solutions
There are a few different ways of handling this issue.
Ask for full photos permission on iOS 14
Edit: This doesn't seem possible on iOS 14
Using the new requesetAuthorizationForAccessLevel method, the plugin could ask for full permissions. This would make the existing resizing and metadata copying work, at the expense of Flutter apps asking for higher permissions than necessary (full access to all photos)
Manually attach the orientation flag to the scaled image's metadata
The image returned from the UIImagePickerController includes the orientation flag, without requiring access to the PHAsset. Therefore, the plugin could just attach the orientation flag as metadata, even if the PHAsset could not be retrieved.
Not hardcoding an orientation when scaling an image, then discarding the orientation flag from the original image.
Edit: this seems to be the Most Correct Solution
The scaling code hard-codes an orientation to prevent the scale from automatically rotating the image. Otherwise, when the orientation metadata is copied from the original image, the final result is incorrect. However, another approach would be to accept the automatic rotation and then disregard the orientation flag when copying the metadata.
Steps to Reproduce
This issue can be replicated using image_picker's example app.
- Have an iOS device with iOS 14+ installed
- Take a photo using the iOS Camera app in portrait orientation
- Run the image_picker example app
- Tap the Gallery button
- Enter a
maxWidthormaxHeight, which will cause the plugin to resize. The dimension entered is not important - Press Pick
- The access request will appear on screen. Tap Select Photos...
- Select the portrait-oriented image you took in step 1
- Press Done
- Select the portrait-oriented image again (Apple's UI for this is a bit clunky)
- The image you selected will be shown rotated 90 degrees
Expected results:
The photo should be displayed on screen in the correct orientation. This screenshot was generated using the steps above, but omitting the dimension, so no resize occurs.
Actual results:
This is affecting real users of our app today. As a workaround we are going to modify the plugin to ignore the original metadata and just bake the rotation into the scaled image. However, I'd be happy to help resolving this issue, depending on the preferred path.
Details
[✓] Flutter (Channel master, 1.26.0-2.0.pre.356, on Mac OS X 10.15.7 19H2 darwin-x64, locale en-AU)
• Flutter version 1.26.0-2.0.pre.356 at /Users/michael/.bin/flutter
• Framework revision 8d72307c47 (3 hours ago), 2021-01-14 16:59:04 -0800
• Engine revision effb529ece
• Dart version 2.12.0 (build 2.12.0-224.0.dev)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
• Android SDK at /Users/michael/Library/Android/sdk
• Platform android-29, build-tools 29.0.3
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
• All Android licenses accepted.
[!] Xcode - develop for iOS and macOS
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 12.3, Build version 12C33
! CocoaPods 1.9.3 out of date (1.10.0 is recommended).
CocoaPods is used to retrieve the iOS and macOS platform side's plugin code that responds to your plugin usage on the Dart side.
Without CocoaPods, plugins will not work on iOS or macOS.
For more info, see https://flutter.dev/platform-plugins
To upgrade see https://guides.cocoapods.org/using/getting-started.html#installation for instructions.
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 3.6)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 45.1.1
• Dart plugin version 192.7761
• Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
[✓] VS Code (version 1.52.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.18.1
[✓] Connected device (3 available)
• iPhone (mobile) • c3ee8096a2df059e475378199066cd0fec8b6a48 • ios • iOS 14.3
• macOS (desktop) • macos • darwin-x64 • Mac OS X 10.15.7 19H2 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 87.0.4280.141
! Doctor found issues in 1 category.

