New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for PHPickerViewController
#1654
Conversation
|
||
@import MobileCoreServices; | ||
|
||
@interface ImagePickerManager () | ||
|
||
@property (nonatomic, strong) UIImagePickerController *picker; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The presenting view controller retains the picker so there's no requirement to hold on to it
@@ -198,12 +215,14 @@ - (void)checkCameraPermissions:(void(^)(BOOL granted))callback | |||
if (status == AVAuthorizationStatusAuthorized) { | |||
callback(YES); | |||
return; | |||
} else if (status == AVAuthorizationStatusNotDetermined){ | |||
} | |||
else if (status == AVAuthorizationStatusNotDetermined){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a mix of formatting
Thanks for the PR, I'll see if I can squeeze in some testing soon and revert back. |
@JanGorman I have added few comments, can you check it and revert with changes or your POV |
@ravirajn22 Thanks for the comments. Agreed! I factored out the common pieces |
Need to check the PR again, will update and push before this weekend. |
@Johan-dutoit Thanks for the feedback. I'll see how to work around that |
ios/ImagePickerManager.m
Outdated
} | ||
|
||
// Immediately dismiss the controller to prevent calling the completion handler twice | ||
[picker dismissViewControllerAnimated:YES completion:nil]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Johan-dutoit This change here fixes the double tap issue in my tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does a better job, but still fails if you click really fast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You really want to try to break it 😅 I would suggest you make a call on how realistic this is (double tap speed vs. mouse clicks…). I don't see what other way there is to prevent this.
What's interesting is that it's reproducible (at least for me) only on the first double click. Subsequent attempts fail until you relaunch it. I'm able to reproduce this in a bare bones example and will report it as a bug to Apple.
class ViewController: UIViewController {
@IBAction func pick(_ sender: Any) {
let picker = PHPickerViewController(configuration: .init())
picker.delegate = self
present(picker, animated: true)
}
}
extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
// Prints twice the first time you double click really quickly
print("Called")
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reported to Apple as FB9059292
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I totally agree with your sentiments above. At first I didn't even want to mention it, but decided that it's good to have a trail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the call to dismissViewControllerAnimated
be made before we check for results? That's what's suggested in the WWDC video about PHPicker, I'm wondering if that has incidence in the bug you're facing with double/triple taps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@flochtililoch Thanks for the comment. I made this change specifically to attempt to work around the mentioned bug. In my tests it didn't matter whether you call the completion block in the dismiss completion block or outside. It was always reproducible the first time that you present the picker but not on the second call which to me suggest that there is an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, what I meant was:
// First, dismiss picker
[picker dismissViewControllerAnimated:YES completion:nil];
// Then, check results
if (results.count == 0) {
dispatch_async(dispatch_get_main_queue(), ^{
[picker dismissViewControllerAnimated:YES completion:^{
self.callback(@[@{@"didCancel": @YES}]);
}];
});
return;
}
...
Another thing worthwhile to explore, is around setting the flag preferredAssetRepresentationMode
on the configuration object to PHPickerConfigurationAssetRepresentationModeCurrent
. This blog article suggests that not setting it can have a performance impact on the import process.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's give this a go, would love to not face this :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved it to the first line of the delegate callback. The double tap problem persists but in terms of consistency I guess this is better (only one dismiss instead of separate handling for the "cancel" case).
I also applied the suggestion to set the preferredAssetRepresentationMode
if @ravirajn22 is happy with the code, I'm happy to give the thumbs up. |
Sorry for the delay, haven't looked into the code. Give me few more days. |
Does this PR allow users of the library to set the selection limit, in order to select multiple images at once? |
@Sven65 It does not. The library currently doesn't offer this option either. It's straightforward to add that option but IMO better done in a separate PR |
@ravirajn22 Have you had chance to review? If there's anything I can do to help unblock the PR please let me know 🙂 |
@ravirajn22 last chances for a review, going to merge this today, unless there are any objections. |
@Johan-dutoit I will review and tell before tomorrow. This change affects many lines including styling and refactor of unrelated lines. I don't know if its the right way to add feature + refactor together. Just give some time. |
@JanGorman I have refactored your code, it now uses one function to process image (same for video), reducing duplicate codes and fixed few issues. Your code did not handle image types other than jpg and png. Please review this. @Johan-dutoit Test this code and release a beta version if everything ok. |
Regarding this error, we can handle this in another PR. Just setting the callback to nil after sending response and checking if callback != nil before sending the response should fix this issue. This issue exist even in current production build I think. |
|
||
typedef NS_ENUM(NSInteger, RNImagePickerTarget) { | ||
camera = 1, | ||
library | ||
}; | ||
|
||
@interface ImagePickerManager : NSObject <RCTBridgeModule, UINavigationControllerDelegate, UIActionSheetDelegate, UIImagePickerControllerDelegate> | ||
@interface ImagePickerManager : NSObject <RCTBridgeModule, UINavigationControllerDelegate, UIActionSheetDelegate, UIImagePickerControllerDelegate, PHPickerViewControllerDelegate> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ravirajn22 Out of curiosity, why do you prefer declaring this on the public interface? For one, the outside world doesn't need to know that it conforms to the interface (personally I'd move UIActionSheetDelegate
, UIImagePickerControllerDelegate
, UINavigationControllerDelegate
into the .m
file too). This was one of the benefits that Obj-C has over Swift, that you can move the #import
into the .m
file making the link phase faster
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am no expert in objective c, so l like consistency over others. Since all previous protocols have been done on the public interface as you said, I thought its better if we can declare PHPickerViewControllerDelegate the same way.
Refactors can go in another PR or with smaller PR, not with big PR like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood! Then I'll gladly change this in a follow-up PR 👍
|
||
- (void)onImageObtained:(UIImage*)image data:(NSData*)data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Come to think of it, we don't necessarily need to pass the UIImage
here. We could
UIImage *image = [[UIImage alloc] initWithData:data];
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine to put this into a follow-up PR as well imo
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes ideally it should be done as you said. But in UIImagePickerController there is something called edited UIIImage and original UIImage. But the data we obtain is only from original image, you cannot get edited image data. So if the user edited the image then we need to process further using the edited UIImage and use the data from original image to just obtain image type and save it directly for GIF type. I am not sure if I am explaining properly.
This is how previous code has been working so I just used it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll look into it. Should be possible to figure out the type and do the editing as you said
LGTM. As discussed in the PR I'll follow up with a refactoring to move protocol conformance into categories akin to class extensions in Swift |
Thanks @JanGorman, I'll do some basic testing locally and get this merged. |
@Johan-dutoit Is there anything left that I can help with to merge? :) |
# [3.6.0](v3.5.0...v3.6.0) (2021-05-08) ### Features * add support for PHPickerViewController ([#1654](#1654)) ([26eee5d](26eee5d))
🎉 This PR is included in version 3.6.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
# [3.6.0](react-native-image-picker/react-native-image-picker@v3.5.0...v3.6.0) (2021-05-08) ### Features * add support for PHPickerViewController ([#1654](react-native-image-picker/react-native-image-picker#1654)) ([26eee5d](react-native-image-picker/react-native-image-picker@26eee5d))
Does this support the |
# [3.6.0](react-native-image-picker/react-native-image-picker@v3.5.0...v3.6.0) (2021-05-08) ### Features * add support for PHPickerViewController ([#1654](react-native-image-picker/react-native-image-picker#1654)) ([0822468](react-native-image-picker/react-native-image-picker@0822468))
Motivation
This PR adds support for the iOS 14
PHPickerViewController
to address Issue #1428Test Plan
Tested using the included
example
app to load images/videos and verify that the output matches that of the original.