react-native/Libraries/CameraRoll/RCTImagePickerManager.m

179 lines
5.7 KiB
Objective-C

/*
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
#import "RCTImagePickerManager.h"
#import <MobileCoreServices/UTCoreTypes.h>
#import <UIKit/UIKit.h>
#import <React/RCTConvert.h>
#import <React/RCTImageStoreManager.h>
#import <React/RCTRootView.h>
#import <React/RCTUtils.h>
@interface RCTImagePickerManager () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@end
@implementation RCTImagePickerManager
{
NSMutableArray<UIImagePickerController *> *_pickers;
NSMutableArray<RCTResponseSenderBlock> *_pickerCallbacks;
NSMutableArray<RCTResponseSenderBlock> *_pickerCancelCallbacks;
}
RCT_EXPORT_MODULE(ImagePickerIOS);
@synthesize bridge = _bridge;
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
RCT_EXPORT_METHOD(canRecordVideos:(RCTResponseSenderBlock)callback)
{
NSArray<NSString *> *availableMediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
callback(@[@([availableMediaTypes containsObject:(NSString *)kUTTypeMovie])]);
}
RCT_EXPORT_METHOD(canUseCamera:(RCTResponseSenderBlock)callback)
{
callback(@[@([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])]);
}
RCT_EXPORT_METHOD(openCameraDialog:(NSDictionary *)config
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
{
if (RCTRunningInAppExtension()) {
cancelCallback(@[@"Camera access is unavailable in an app extension"]);
return;
}
UIImagePickerController *imagePicker = [UIImagePickerController new];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
if ([RCTConvert BOOL:config[@"videoMode"]]) {
imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
}
[self _presentPicker:imagePicker
successCallback:callback
cancelCallback:cancelCallback];
}
RCT_EXPORT_METHOD(openSelectDialog:(NSDictionary *)config
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback)
{
if (RCTRunningInAppExtension()) {
cancelCallback(@[@"Image picker is currently unavailable in an app extension"]);
return;
}
UIImagePickerController *imagePicker = [UIImagePickerController new];
imagePicker.delegate = self;
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
NSMutableArray<NSString *> *allowedTypes = [NSMutableArray new];
if ([RCTConvert BOOL:config[@"showImages"]]) {
[allowedTypes addObject:(NSString *)kUTTypeImage];
}
if ([RCTConvert BOOL:config[@"showVideos"]]) {
[allowedTypes addObject:(NSString *)kUTTypeMovie];
}
imagePicker.mediaTypes = allowedTypes;
[self _presentPicker:imagePicker
successCallback:callback
cancelCallback:cancelCallback];
}
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<NSString *, id> *)info
{
NSString *mediaType = info[UIImagePickerControllerMediaType];
BOOL isMovie = [mediaType isEqualToString:(NSString *)kUTTypeMovie];
NSString *key = isMovie ? UIImagePickerControllerMediaURL : UIImagePickerControllerReferenceURL;
NSURL *imageURL = info[key];
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSNumber *width = 0;
NSNumber *height = 0;
if (image) {
height = @(image.size.height);
width = @(image.size.width);
}
if (imageURL) {
[self _dismissPicker:picker args:@[imageURL.absoluteString, RCTNullIfNil(height), RCTNullIfNil(width)]];
return;
}
// This is a newly taken image, and doesn't have a URL yet.
// We need to save it to the image store first.
UIImage *originalImage = info[UIImagePickerControllerOriginalImage];
// WARNING: Using ImageStoreManager may cause a memory leak because the
// image isn't automatically removed from store once we're done using it.
[_bridge.imageStoreManager storeImage:originalImage withBlock:^(NSString *tempImageTag) {
[self _dismissPicker:picker args:tempImageTag ? @[tempImageTag, RCTNullIfNil(height), RCTNullIfNil(width)] : nil];
}];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self _dismissPicker:picker args:nil];
}
- (void)_presentPicker:(UIImagePickerController *)imagePicker
successCallback:(RCTResponseSenderBlock)callback
cancelCallback:(RCTResponseSenderBlock)cancelCallback
{
if (!_pickers) {
_pickers = [NSMutableArray new];
_pickerCallbacks = [NSMutableArray new];
_pickerCancelCallbacks = [NSMutableArray new];
}
[_pickers addObject:imagePicker];
[_pickerCallbacks addObject:callback];
[_pickerCancelCallbacks addObject:cancelCallback];
UIViewController *rootViewController = RCTPresentedViewController();
[rootViewController presentViewController:imagePicker animated:YES completion:nil];
}
- (void)_dismissPicker:(UIImagePickerController *)picker args:(NSArray *)args
{
NSUInteger index = [_pickers indexOfObject:picker];
if (index == NSNotFound) {
// This happens if the user selects multiple items in succession.
return;
}
RCTResponseSenderBlock successCallback = _pickerCallbacks[index];
RCTResponseSenderBlock cancelCallback = _pickerCancelCallbacks[index];
[_pickers removeObjectAtIndex:index];
[_pickerCallbacks removeObjectAtIndex:index];
[_pickerCancelCallbacks removeObjectAtIndex:index];
UIViewController *rootViewController = RCTPresentedViewController();
[rootViewController dismissViewControllerAnimated:YES completion:nil];
if (args) {
successCallback(args);
} else {
cancelCallback(@[]);
}
}
@end