@hotta/react-native-imagepicker 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NativeImagePicker.podspec +21 -0
- package/README.md +20 -0
- package/android/build.gradle +95 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/htreactnative/imagepicker/GlideEngine.kt +85 -0
- package/android/src/main/java/com/htreactnative/imagepicker/NativeImagePickerModule.kt +91 -0
- package/android/src/main/java/com/htreactnative/imagepicker/NativeImagePickerPackage.kt +33 -0
- package/ios/NSDictionary+SYSafeConvert.h +29 -0
- package/ios/NSDictionary+SYSafeConvert.m +92 -0
- package/ios/NativeImagePicker.h +7 -0
- package/ios/NativeImagePicker.mm +458 -0
- package/lib/module/NativeImagePicker.js +62 -0
- package/lib/module/NativeImagePicker.js.map +1 -0
- package/lib/module/index.js +10 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeImagePicker.d.ts +76 -0
- package/lib/typescript/src/NativeImagePicker.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +171 -0
- package/src/NativeImagePicker.ts +79 -0
- package/src/index.tsx +9 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
#import "NativeImagePicker.h"
|
|
2
|
+
#import "NSDictionary+SYSafeConvert.h"
|
|
3
|
+
#import "TZImageCropManager.h"
|
|
4
|
+
#import <AssetsLibrary/AssetsLibrary.h>
|
|
5
|
+
#import <React/RCTUtils.h>
|
|
6
|
+
|
|
7
|
+
@interface NativeImagePicker ()
|
|
8
|
+
|
|
9
|
+
@property (nonatomic, strong) UIImagePickerController *imagePickerVc;
|
|
10
|
+
/**
|
|
11
|
+
保存Promise的resolve block
|
|
12
|
+
*/
|
|
13
|
+
@property (nonatomic, copy) RCTPromiseResolveBlock resolveBlock;
|
|
14
|
+
/**
|
|
15
|
+
保存Promise的reject block
|
|
16
|
+
*/
|
|
17
|
+
@property (nonatomic, copy) RCTPromiseRejectBlock rejectBlock;
|
|
18
|
+
/**
|
|
19
|
+
保存回调的callback
|
|
20
|
+
*/
|
|
21
|
+
@property (nonatomic, copy) RCTResponseSenderBlock callback;
|
|
22
|
+
/**
|
|
23
|
+
保存选中的图片数组
|
|
24
|
+
*/
|
|
25
|
+
@property (nonatomic, strong) NSMutableArray *selectedAssets;
|
|
26
|
+
@end
|
|
27
|
+
@implementation NativeImagePicker
|
|
28
|
+
{
|
|
29
|
+
std::optional<JS::NativeImagePicker::ImagePickerOption> _cameraOptions;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
- (instancetype)init {
|
|
33
|
+
self = [super init];
|
|
34
|
+
if (self) {
|
|
35
|
+
_selectedAssets = [NSMutableArray array];
|
|
36
|
+
}
|
|
37
|
+
return self;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
41
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
42
|
+
{
|
|
43
|
+
return std::make_shared<facebook::react::NativeImagePickerSpecJSI>(params);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
+ (NSString *)moduleName
|
|
47
|
+
{
|
|
48
|
+
return @"NativeImagePicker";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
- (void)dealloc {
|
|
52
|
+
_selectedAssets = nil;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
- (void)asyncShowImagePicker:(JS::NativeImagePicker::ImagePickerOption &)options resolve:(nonnull RCTPromiseResolveBlock)resolve reject:(nonnull RCTPromiseRejectBlock)reject {
|
|
56
|
+
_cameraOptions = options;
|
|
57
|
+
self.resolveBlock = resolve;
|
|
58
|
+
self.rejectBlock = reject;
|
|
59
|
+
self.callback = nil;
|
|
60
|
+
[self openImagePicker];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
- (void)openImagePicker {
|
|
64
|
+
// 照片最大可选张数
|
|
65
|
+
NSInteger imageCount = _cameraOptions->imageCount();
|
|
66
|
+
// 显示内部拍照按钮
|
|
67
|
+
BOOL isCamera = _cameraOptions->isCamera();
|
|
68
|
+
BOOL isCrop = _cameraOptions->isCrop();
|
|
69
|
+
BOOL isGif = _cameraOptions->isGif();
|
|
70
|
+
BOOL showCropCircle = _cameraOptions->showCropCircle();
|
|
71
|
+
BOOL isRecordSelected = _cameraOptions->isRecordSelected();
|
|
72
|
+
BOOL allowPickingOriginalPhoto = _cameraOptions->allowPickingOriginalPhoto();
|
|
73
|
+
BOOL allowPickingMultipleVideo = _cameraOptions->allowPickingMultipleVideo();
|
|
74
|
+
BOOL sortAscendingByModificationDate = _cameraOptions->sortAscendingByModificationDate();
|
|
75
|
+
BOOL showSelectedIndex = _cameraOptions->showSelectedIndex();
|
|
76
|
+
NSInteger CropW = _cameraOptions->CropW();
|
|
77
|
+
NSInteger CropH = _cameraOptions->CropH();
|
|
78
|
+
NSInteger circleCropRadius = _cameraOptions->circleCropRadius();
|
|
79
|
+
NSInteger quality = _cameraOptions->quality();
|
|
80
|
+
|
|
81
|
+
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:imageCount delegate:self];
|
|
82
|
+
imagePickerVc.iconThemeColor = [UIColor colorWithRed:255.0/255.0 green:177.0/255.0 blue:82.0/255.0 alpha:1.0];
|
|
83
|
+
imagePickerVc.maxImagesCount = imageCount;
|
|
84
|
+
imagePickerVc.allowPickingGif = isGif; // 允许GIF
|
|
85
|
+
imagePickerVc.allowTakePicture = isCamera; // 允许用户在内部拍照
|
|
86
|
+
imagePickerVc.allowPickingVideo = NO; // 不允许视频
|
|
87
|
+
imagePickerVc.showSelectedIndex = showSelectedIndex;
|
|
88
|
+
imagePickerVc.allowPickingOriginalPhoto = allowPickingOriginalPhoto; // 允许原图
|
|
89
|
+
imagePickerVc.sortAscendingByModificationDate = sortAscendingByModificationDate;
|
|
90
|
+
imagePickerVc.alwaysEnableDoneBtn = YES;
|
|
91
|
+
imagePickerVc.allowPickingMultipleVideo = isGif ? YES : allowPickingMultipleVideo;
|
|
92
|
+
imagePickerVc.allowCrop = isCrop; // 裁剪
|
|
93
|
+
imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen;
|
|
94
|
+
imagePickerVc.oKButtonTitleColorNormal = [UIColor colorWithRed:255.0/255.0 green:177.0/255.0 blue:82.0/255.0 alpha:1.0];
|
|
95
|
+
if (isRecordSelected) {
|
|
96
|
+
imagePickerVc.selectedAssets = self.selectedAssets; // 当前已选中的图片
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (imageCount == 1) {
|
|
100
|
+
// 单选模式
|
|
101
|
+
imagePickerVc.showSelectBtn = NO;
|
|
102
|
+
|
|
103
|
+
if(isCrop){
|
|
104
|
+
if(showCropCircle) {
|
|
105
|
+
imagePickerVc.needCircleCrop = showCropCircle; //圆形裁剪
|
|
106
|
+
imagePickerVc.circleCropRadius = circleCropRadius; //圆形半径
|
|
107
|
+
} else {
|
|
108
|
+
CGFloat x = ([[UIScreen mainScreen] bounds].size.width - CropW) / 2;
|
|
109
|
+
CGFloat y = ([[UIScreen mainScreen] bounds].size.height - CropH) / 2;
|
|
110
|
+
imagePickerVc.cropRect = CGRectMake(x,y,CropW,CropH);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
__weak TZImagePickerController *weakPicker = imagePickerVc;
|
|
116
|
+
[imagePickerVc setDidFinishPickingPhotosWithInfosHandle:^(NSArray<UIImage *> *photos,NSArray *assets,BOOL isSelectOriginalPhoto,NSArray<NSDictionary *> *infos) {
|
|
117
|
+
if (isRecordSelected) {
|
|
118
|
+
self.selectedAssets = [NSMutableArray arrayWithArray:assets];
|
|
119
|
+
}
|
|
120
|
+
[weakPicker showProgressHUD];
|
|
121
|
+
if (imageCount == 1 && isCrop) {
|
|
122
|
+
[self invokeSuccessWithResult:@[[self handleCropImage:photos[0] phAsset:assets[0] quality:quality]]];
|
|
123
|
+
} else {
|
|
124
|
+
[infos enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
125
|
+
[self handleAssets:assets photos:photos quality:quality isSelectOriginalPhoto:isSelectOriginalPhoto completion:^(NSArray *selecteds) {
|
|
126
|
+
[self invokeSuccessWithResult:selecteds];
|
|
127
|
+
} fail:^(NSError *error) {
|
|
128
|
+
|
|
129
|
+
}];
|
|
130
|
+
}];
|
|
131
|
+
}
|
|
132
|
+
[weakPicker hideProgressHUD];
|
|
133
|
+
}];
|
|
134
|
+
|
|
135
|
+
__weak TZImagePickerController *weakPickerVc = imagePickerVc;
|
|
136
|
+
[imagePickerVc setImagePickerControllerDidCancelHandle:^{
|
|
137
|
+
[self invokeError];
|
|
138
|
+
[weakPickerVc hideProgressHUD];
|
|
139
|
+
}];
|
|
140
|
+
|
|
141
|
+
[[self topViewController] presentViewController:imagePickerVc animated:YES completion:nil];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (UIImagePickerController *)imagePickerVc {
|
|
145
|
+
if (_imagePickerVc == nil) {
|
|
146
|
+
_imagePickerVc = [[UIImagePickerController alloc] init];
|
|
147
|
+
_imagePickerVc.delegate = self;
|
|
148
|
+
}
|
|
149
|
+
return _imagePickerVc;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#pragma mark - UIImagePickerController
|
|
153
|
+
- (void)takePhoto {
|
|
154
|
+
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
|
|
155
|
+
if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) {
|
|
156
|
+
// 无相机权限 做一个友好的提示
|
|
157
|
+
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"无法使用相机" message:@"请在iPhone的""设置-隐私-相机""中允许访问相机" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"设置", nil];
|
|
158
|
+
[alert show];
|
|
159
|
+
} else if (authStatus == AVAuthorizationStatusNotDetermined) {
|
|
160
|
+
// fix issue 466, 防止用户首次拍照拒绝授权时相机页黑屏
|
|
161
|
+
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
|
|
162
|
+
if (granted) {
|
|
163
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
164
|
+
[self takePhoto];
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}];
|
|
168
|
+
// 拍照之前还需要检查相册权限
|
|
169
|
+
} else if ([PHPhotoLibrary authorizationStatus] == 2) { // 已被拒绝,没有相册权限,将无法保存拍的照片
|
|
170
|
+
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"无法访问相册" message:@"请在iPhone的""设置-隐私-相册""中允许访问相册" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"设置", nil];
|
|
171
|
+
[alert show];
|
|
172
|
+
} else if ([PHPhotoLibrary authorizationStatus] == 0) { // 未请求过相册权限
|
|
173
|
+
[[TZImageManager manager] requestAuthorizationWithCompletion:^{
|
|
174
|
+
[self takePhoto];
|
|
175
|
+
}];
|
|
176
|
+
} else {
|
|
177
|
+
[self pushImagePickerController];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 调用相机
|
|
182
|
+
- (void)pushImagePickerController {
|
|
183
|
+
UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
|
|
184
|
+
if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
|
|
185
|
+
self.imagePickerVc.sourceType = sourceType;
|
|
186
|
+
[[self topViewController] presentViewController:self.imagePickerVc animated:YES completion:nil];
|
|
187
|
+
} else {
|
|
188
|
+
NSLog(@"模拟器中无法打开照相机,请在真机中使用");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
|
|
193
|
+
[picker dismissViewControllerAnimated:YES completion:^{
|
|
194
|
+
NSString *type = [info objectForKey:UIImagePickerControllerMediaType];
|
|
195
|
+
if ([type isEqualToString:@"public.image"]) {
|
|
196
|
+
|
|
197
|
+
TZImagePickerController *tzImagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:nil];
|
|
198
|
+
tzImagePickerVc.sortAscendingByModificationDate = NO;
|
|
199
|
+
[tzImagePickerVc showProgressHUD];
|
|
200
|
+
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
|
|
201
|
+
|
|
202
|
+
// save photo and get asset / 保存图片,获取到asset
|
|
203
|
+
[[TZImageManager manager] savePhotoWithImage:image location:NULL completion:^(PHAsset *asset, NSError *error){
|
|
204
|
+
if (error) {
|
|
205
|
+
[tzImagePickerVc hideProgressHUD];
|
|
206
|
+
NSLog(@"图片保存失败 %@",error);
|
|
207
|
+
} else {
|
|
208
|
+
[tzImagePickerVc hideProgressHUD];
|
|
209
|
+
|
|
210
|
+
TZAssetModel *assetModel = [[TZImageManager manager] createModelWithAsset:asset];
|
|
211
|
+
BOOL isCrop = self->_cameraOptions->isCrop();
|
|
212
|
+
BOOL showCropCircle = self->_cameraOptions->showCropCircle();
|
|
213
|
+
NSInteger CropW = self->_cameraOptions->CropW();
|
|
214
|
+
NSInteger CropH = self->_cameraOptions->CropH();
|
|
215
|
+
NSInteger circleCropRadius = self->_cameraOptions->circleCropRadius();
|
|
216
|
+
NSInteger quality = self->_cameraOptions->quality();
|
|
217
|
+
|
|
218
|
+
if (isCrop) {
|
|
219
|
+
TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initCropTypeWithAsset:assetModel.asset photo:image completion:^(UIImage *cropImage, id asset) {
|
|
220
|
+
[self invokeSuccessWithResult:@[[self handleCropImage:cropImage phAsset:asset quality:quality]]];
|
|
221
|
+
}];
|
|
222
|
+
imagePicker.allowPickingImage = YES;
|
|
223
|
+
if(showCropCircle) {
|
|
224
|
+
imagePicker.needCircleCrop = showCropCircle; //圆形裁剪
|
|
225
|
+
imagePicker.circleCropRadius = circleCropRadius; //圆形半径
|
|
226
|
+
} else {
|
|
227
|
+
CGFloat x = ([[UIScreen mainScreen] bounds].size.width - CropW) / 2;
|
|
228
|
+
CGFloat y = ([[UIScreen mainScreen] bounds].size.height - CropH) / 2;
|
|
229
|
+
imagePicker.cropRect = CGRectMake(x,y,CropW,CropH);
|
|
230
|
+
}
|
|
231
|
+
[[self topViewController] presentViewController:imagePicker animated:YES completion:nil];
|
|
232
|
+
} else {
|
|
233
|
+
[self invokeSuccessWithResult:@[[self handleCropImage:image phAsset:asset quality:quality]]];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}];
|
|
237
|
+
}
|
|
238
|
+
}];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
|
|
242
|
+
[self invokeError];
|
|
243
|
+
if ([picker isKindOfClass:[UIImagePickerController class]]) {
|
|
244
|
+
[picker dismissViewControllerAnimated:YES completion:nil];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
#pragma mark - UIAlertViewDelegate
|
|
249
|
+
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
|
|
250
|
+
if (buttonIndex == 1) { // 去设置界面,开启相机访问权限
|
|
251
|
+
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
- (BOOL)isAssetCanSelect:(PHAsset *)asset {
|
|
256
|
+
BOOL allowPickingGif = _cameraOptions->isGif();
|
|
257
|
+
BOOL isGIF = [[TZImageManager manager] getAssetType:asset] == TZAssetModelMediaTypePhotoGif;
|
|
258
|
+
if (!allowPickingGif && isGIF) {
|
|
259
|
+
return NO;
|
|
260
|
+
}
|
|
261
|
+
return YES;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/// 异步处理获取图片
|
|
265
|
+
- (void)handleAssets:(NSArray *)assets photos:(NSArray*)photos quality:(CGFloat)quality isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto completion:(void (^)(NSArray *selecteds))completion fail:(void(^)(NSError *error))fail {
|
|
266
|
+
NSMutableArray *selectedPhotos = [NSMutableArray array];
|
|
267
|
+
|
|
268
|
+
[assets enumerateObjectsUsingBlock:^(PHAsset * _Nonnull asset, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
269
|
+
if (asset.mediaType == PHAssetMediaTypeVideo) {
|
|
270
|
+
[[TZImageManager manager] getVideoOutputPathWithAsset:asset presetName:AVAssetExportPresetHighestQuality success:^(NSString *outputPath) {
|
|
271
|
+
[selectedPhotos addObject:[self handleVideoData:outputPath asset:asset coverImage:photos[idx] quality:quality]];
|
|
272
|
+
if ([selectedPhotos count] == [assets count]) {
|
|
273
|
+
completion(selectedPhotos);
|
|
274
|
+
}
|
|
275
|
+
if (idx + 1 == [assets count] && [selectedPhotos count] != [assets count]) {
|
|
276
|
+
fail(nil);
|
|
277
|
+
}
|
|
278
|
+
} failure:^(NSString *errorMessage, NSError *error) {
|
|
279
|
+
|
|
280
|
+
}];
|
|
281
|
+
} else {
|
|
282
|
+
BOOL isGIF = [[TZImageManager manager] getAssetType:asset] == TZAssetModelMediaTypePhotoGif;
|
|
283
|
+
if (isGIF || isSelectOriginalPhoto) {
|
|
284
|
+
[[TZImageManager manager] requestImageDataForAsset:asset completion:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
|
|
285
|
+
[selectedPhotos addObject:[self handleOriginalPhotoData:imageData phAsset:asset isGIF:isGIF quality:quality]];
|
|
286
|
+
if ([selectedPhotos count] == [assets count]) {
|
|
287
|
+
completion(selectedPhotos);
|
|
288
|
+
}
|
|
289
|
+
if (idx + 1 == [assets count] && [selectedPhotos count] != [assets count]) {
|
|
290
|
+
fail(nil);
|
|
291
|
+
}
|
|
292
|
+
} progressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
|
|
293
|
+
|
|
294
|
+
}];
|
|
295
|
+
} else {
|
|
296
|
+
[selectedPhotos addObject:[self handleCropImage:photos[idx] phAsset:asset quality:quality]];
|
|
297
|
+
if ([selectedPhotos count] == [assets count]) {
|
|
298
|
+
completion(selectedPhotos);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/// 处理裁剪图片数据
|
|
306
|
+
- (NSDictionary *)handleCropImage:(UIImage *)image phAsset:(PHAsset *)phAsset quality:(CGFloat)quality {
|
|
307
|
+
[self createDir];
|
|
308
|
+
|
|
309
|
+
NSMutableDictionary *photo = [NSMutableDictionary dictionary];
|
|
310
|
+
NSString *filename = [NSString stringWithFormat:@"%@%@", [[NSUUID UUID] UUIDString], [phAsset valueForKey:@"filename"]];
|
|
311
|
+
NSString *fileExtension = [filename pathExtension];
|
|
312
|
+
NSMutableString *filePath = [NSMutableString string];
|
|
313
|
+
BOOL isPNG = [fileExtension hasSuffix:@"PNG"] || [fileExtension hasSuffix:@"png"];
|
|
314
|
+
BOOL compressFocusAlpha = _cameraOptions->compressFocusAlpha();
|
|
315
|
+
|
|
316
|
+
if (isPNG) {
|
|
317
|
+
[filePath appendString:[NSString stringWithFormat:@"%@SyanImageCaches/%@", NSTemporaryDirectory(), filename]];
|
|
318
|
+
} else {
|
|
319
|
+
[filePath appendString:[NSString stringWithFormat:@"%@SyanImageCaches/%@.jpg", NSTemporaryDirectory(), [filename stringByDeletingPathExtension]]];
|
|
320
|
+
}
|
|
321
|
+
//UIImagePNGRepresentation压缩压缩率太低了可以使用 pngquant
|
|
322
|
+
NSData *writeData = (isPNG && compressFocusAlpha) ? UIImagePNGRepresentation(image) : UIImageJPEGRepresentation(image, quality/100);
|
|
323
|
+
[writeData writeToFile:filePath atomically:YES];
|
|
324
|
+
|
|
325
|
+
photo[@"uri"] = filePath;
|
|
326
|
+
photo[@"width"] = @(image.size.width);
|
|
327
|
+
photo[@"height"] = @(image.size.height);
|
|
328
|
+
NSInteger size = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil].fileSize;
|
|
329
|
+
photo[@"size"] = @(size);
|
|
330
|
+
photo[@"mediaType"] = @(phAsset.mediaType);
|
|
331
|
+
if (_cameraOptions->enableBase64()) {
|
|
332
|
+
if(isPNG){
|
|
333
|
+
photo[@"base64"] = [NSString stringWithFormat:@"data:image/png;base64,%@", [writeData base64EncodedStringWithOptions:0]];
|
|
334
|
+
}else{
|
|
335
|
+
photo[@"base64"] = [NSString stringWithFormat:@"data:image/jpeg;base64,%@", [writeData base64EncodedStringWithOptions:0]];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return photo;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/// 处理原图数据
|
|
343
|
+
- (NSDictionary *)handleOriginalPhotoData:(NSData *)data phAsset:(PHAsset *)phAsset isGIF:(BOOL)isGIF quality:(CGFloat)quality {
|
|
344
|
+
[self createDir];
|
|
345
|
+
|
|
346
|
+
NSMutableDictionary *photo = [NSMutableDictionary dictionary];
|
|
347
|
+
NSString *filename = [NSString stringWithFormat:@"%@%@", [[NSUUID UUID] UUIDString], [phAsset valueForKey:@"filename"]];
|
|
348
|
+
NSString *fileExtension = [filename pathExtension];
|
|
349
|
+
UIImage *image = nil;
|
|
350
|
+
NSData *writeData = nil;
|
|
351
|
+
NSMutableString *filePath = [NSMutableString string];
|
|
352
|
+
BOOL isPNG = [fileExtension hasSuffix:@"PNG"] || [fileExtension hasSuffix:@"png"];
|
|
353
|
+
BOOL compressFocusAlpha = _cameraOptions->compressFocusAlpha();
|
|
354
|
+
|
|
355
|
+
if (isGIF) {
|
|
356
|
+
image = [UIImage sd_tz_animatedGIFWithData:data];
|
|
357
|
+
writeData = data;
|
|
358
|
+
} else {
|
|
359
|
+
image = [UIImage imageWithData: data];
|
|
360
|
+
writeData = (isPNG && compressFocusAlpha) ? UIImagePNGRepresentation(image) : UIImageJPEGRepresentation(image, quality/100);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (isPNG || isGIF) {
|
|
364
|
+
[filePath appendString:[NSString stringWithFormat:@"%@SyanImageCaches/%@", NSTemporaryDirectory(), filename]];
|
|
365
|
+
} else {
|
|
366
|
+
[filePath appendString:[NSString stringWithFormat:@"%@SyanImageCaches/%@.jpg", NSTemporaryDirectory(), [filename stringByDeletingPathExtension]]];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
[writeData writeToFile:filePath atomically:YES];
|
|
370
|
+
|
|
371
|
+
photo[@"uri"] = filePath;
|
|
372
|
+
photo[@"width"] = @(image.size.width);
|
|
373
|
+
photo[@"height"] = @(image.size.height);
|
|
374
|
+
NSInteger size = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil].fileSize;
|
|
375
|
+
photo[@"size"] = @(size);
|
|
376
|
+
photo[@"mediaType"] = @(phAsset.mediaType);
|
|
377
|
+
if (_cameraOptions->enableBase64() && !isGIF) {
|
|
378
|
+
if(isPNG){
|
|
379
|
+
photo[@"base64"] = [NSString stringWithFormat:@"data:image/png;base64,%@", [writeData base64EncodedStringWithOptions:0]];
|
|
380
|
+
}else{
|
|
381
|
+
photo[@"base64"] = [NSString stringWithFormat:@"data:image/jpeg;base64,%@", [writeData base64EncodedStringWithOptions:0]];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return photo;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/// 处理视频数据
|
|
389
|
+
- (NSDictionary *)handleVideoData:(NSString *)outputPath asset:(PHAsset *)asset coverImage:(UIImage *)coverImage quality:(CGFloat)quality {
|
|
390
|
+
NSMutableDictionary *video = [NSMutableDictionary dictionary];
|
|
391
|
+
video[@"uri"] = outputPath;
|
|
392
|
+
video[@"fileName"] = [asset valueForKey:@"filename"];
|
|
393
|
+
NSInteger size = [[NSFileManager defaultManager] attributesOfItemAtPath:outputPath error:nil].fileSize;
|
|
394
|
+
video[@"size"] = @(size);
|
|
395
|
+
video[@"duration"] = @(asset.duration);
|
|
396
|
+
video[@"width"] = @(asset.pixelWidth);
|
|
397
|
+
video[@"height"] = @(asset.pixelHeight);
|
|
398
|
+
video[@"type"] = @"video";
|
|
399
|
+
video[@"mime"] = @"video/mp4";
|
|
400
|
+
// iOS only
|
|
401
|
+
video[@"coverUri"] = [self handleCropImage:coverImage phAsset:asset quality:quality][@"uri"];
|
|
402
|
+
video[@"favorite"] = @(asset.favorite);
|
|
403
|
+
video[@"mediaType"] = @(asset.mediaType);
|
|
404
|
+
|
|
405
|
+
return video;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/// 创建SyanImageCaches缓存目录
|
|
409
|
+
- (BOOL)createDir {
|
|
410
|
+
NSString * path = [NSString stringWithFormat:@"%@SyanImageCaches", NSTemporaryDirectory()];;
|
|
411
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
412
|
+
BOOL isDir;
|
|
413
|
+
if (![fileManager fileExistsAtPath:path isDirectory:&isDir]) {
|
|
414
|
+
//先判断目录是否存在,不存在才创建
|
|
415
|
+
BOOL res = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
|
|
416
|
+
return res;
|
|
417
|
+
} else return NO;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
- (void)invokeSuccessWithResult:(NSArray *)photos {
|
|
422
|
+
if (self.callback) {
|
|
423
|
+
self.callback(@[[NSNull null], photos]);
|
|
424
|
+
self.callback = nil;
|
|
425
|
+
}
|
|
426
|
+
if (self.resolveBlock) {
|
|
427
|
+
self.resolveBlock(photos);
|
|
428
|
+
self.resolveBlock = nil;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
- (void)invokeError {
|
|
433
|
+
if (self.callback) {
|
|
434
|
+
self.callback(@[@"取消"]);
|
|
435
|
+
self.callback = nil;
|
|
436
|
+
}
|
|
437
|
+
if (self.rejectBlock) {
|
|
438
|
+
self.rejectBlock(@"", @"取消", nil);
|
|
439
|
+
self.rejectBlock = nil;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
+ (BOOL)requiresMainQueueSetup
|
|
444
|
+
{
|
|
445
|
+
return YES;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
- (dispatch_queue_t)methodQueue
|
|
449
|
+
{
|
|
450
|
+
return dispatch_get_main_queue();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
- (UIViewController *)topViewController {
|
|
454
|
+
UIViewController *rootViewController = RCTPresentedViewController();
|
|
455
|
+
return rootViewController;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
@end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
4
|
+
import { Dimensions } from 'react-native';
|
|
5
|
+
const {
|
|
6
|
+
width
|
|
7
|
+
} = Dimensions.get('window');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 默认参数
|
|
11
|
+
*/
|
|
12
|
+
export const defaultOptions = {
|
|
13
|
+
imageCount: 6,
|
|
14
|
+
// 最大选择图片数目,默认6
|
|
15
|
+
isRecordSelected: false,
|
|
16
|
+
// 是否已选图片
|
|
17
|
+
isCamera: true,
|
|
18
|
+
// 是否允许用户在内部拍照,默认true
|
|
19
|
+
isCrop: false,
|
|
20
|
+
// 是否允许裁剪,默认false, imageCount 为1才生效
|
|
21
|
+
CropW: ~~(width * 0.6),
|
|
22
|
+
// 裁剪宽度,默认屏幕宽度60%
|
|
23
|
+
CropH: ~~(width * 0.6),
|
|
24
|
+
// 裁剪高度,默认屏幕宽度60%
|
|
25
|
+
isGif: false,
|
|
26
|
+
// 是否允许选择GIF,默认false,暂无回调GIF数据
|
|
27
|
+
showCropCircle: false,
|
|
28
|
+
// 是否显示圆形裁剪区域,默认false
|
|
29
|
+
circleCropRadius: ~~(width / 4),
|
|
30
|
+
// 圆形裁剪半径,默认屏幕宽度一半
|
|
31
|
+
showCropFrame: true,
|
|
32
|
+
// 是否显示裁剪区域,默认true
|
|
33
|
+
showCropGrid: false,
|
|
34
|
+
// 是否隐藏裁剪区域网格,默认false
|
|
35
|
+
freeStyleCropEnabled: false,
|
|
36
|
+
// 裁剪框是否可拖拽
|
|
37
|
+
rotateEnabled: true,
|
|
38
|
+
// 裁剪是否可旋转图片
|
|
39
|
+
scaleEnabled: true,
|
|
40
|
+
// 裁剪是否可放大缩小图片
|
|
41
|
+
compress: true,
|
|
42
|
+
compressFocusAlpha: false,
|
|
43
|
+
//压缩png保留通明度
|
|
44
|
+
minimumCompressSize: 100,
|
|
45
|
+
// 小于100kb的图片不压缩
|
|
46
|
+
quality: 90,
|
|
47
|
+
// 压缩质量
|
|
48
|
+
enableBase64: false,
|
|
49
|
+
// 是否返回base64编码,默认不返回
|
|
50
|
+
allowPickingOriginalPhoto: false,
|
|
51
|
+
allowPickingMultipleVideo: false,
|
|
52
|
+
// 可以多选视频/gif/图片,和照片共享最大可选张数maxImagesCount的限制
|
|
53
|
+
videoMaximumDuration: 10 * 60,
|
|
54
|
+
// 视频最大拍摄时间,默认是10分钟,单位是秒
|
|
55
|
+
isWeChatStyle: false,
|
|
56
|
+
// 是否是微信风格选择界面 Android Only
|
|
57
|
+
sortAscendingByModificationDate: true,
|
|
58
|
+
// 对照片排序,按修改时间升序,默认是YES。如果设置为NO,最新的照片会显示在最前面,内部的拍照按钮会排在第一个
|
|
59
|
+
showSelectedIndex: false // 是否显示序号, 默认不显示
|
|
60
|
+
};
|
|
61
|
+
export default TurboModuleRegistry.getEnforcing('NativeImagePicker');
|
|
62
|
+
//# sourceMappingURL=NativeImagePicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","Dimensions","width","get","defaultOptions","imageCount","isRecordSelected","isCamera","isCrop","CropW","CropH","isGif","showCropCircle","circleCropRadius","showCropFrame","showCropGrid","freeStyleCropEnabled","rotateEnabled","scaleEnabled","compress","compressFocusAlpha","minimumCompressSize","quality","enableBase64","allowPickingOriginalPhoto","allowPickingMultipleVideo","videoMaximumDuration","isWeChatStyle","sortAscendingByModificationDate","showSelectedIndex","getEnforcing"],"sourceRoot":"../../src","sources":["NativeImagePicker.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AACpE,SAASC,UAAU,QAAQ,cAAc;AAyCzC,MAAM;EAAEC;AAAM,CAAC,GAAGD,UAAU,CAACE,GAAG,CAAC,QAAQ,CAAC;;AAE1C;AACA;AACA;AACA,OAAO,MAAMC,cAAc,GAAG;EAC1BC,UAAU,EAAE,CAAC;EAAc;EAC3BC,gBAAgB,EAAE,KAAK;EAAI;EAC3BC,QAAQ,EAAE,IAAI;EAAa;EAC3BC,MAAM,EAAE,KAAK;EAAc;EAC3BC,KAAK,EAAE,CAAC,EAAEP,KAAK,GAAG,GAAG,CAAC;EAAK;EAC3BQ,KAAK,EAAE,CAAC,EAAER,KAAK,GAAG,GAAG,CAAC;EAAK;EAC3BS,KAAK,EAAE,KAAK;EAAe;EAC3BC,cAAc,EAAE,KAAK;EAAM;EAC3BC,gBAAgB,EAAE,CAAC,EAAEX,KAAK,GAAG,CAAC,CAAC;EAAE;EACjCY,aAAa,EAAE,IAAI;EAAQ;EAC3BC,YAAY,EAAE,KAAK;EAAQ;EAC3BC,oBAAoB,EAAE,KAAK;EAAE;EAC7BC,aAAa,EAAE,IAAI;EAAQ;EAC3BC,YAAY,EAAE,IAAI;EAAS;EAC3BC,QAAQ,EAAE,IAAI;EACdC,kBAAkB,EAAC,KAAK;EAAI;EAC5BC,mBAAmB,EAAE,GAAG;EAAG;EAC3BC,OAAO,EAAE,EAAE;EAAgB;EAC3BC,YAAY,EAAE,KAAK;EAAQ;EAC3BC,yBAAyB,EAAE,KAAK;EAChCC,yBAAyB,EAAE,KAAK;EAAE;EAClCC,oBAAoB,EAAE,EAAE,GAAG,EAAE;EAAE;EAC/BC,aAAa,EAAE,KAAK;EAAO;EAC3BC,+BAA+B,EAAE,IAAI;EAAE;EACvCC,iBAAiB,EAAE,KAAK,CAAE;AAC9B,CAAC;AAKD,eAAe7B,mBAAmB,CAAC8B,YAAY,CAAO,mBAAmB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import NativeImagePicker from "./NativeImagePicker.js";
|
|
4
|
+
const HTNativeImagePicker = (() => {
|
|
5
|
+
return {
|
|
6
|
+
asyncShowImagePicker: NativeImagePicker.asyncShowImagePicker
|
|
7
|
+
};
|
|
8
|
+
})();
|
|
9
|
+
export default HTNativeImagePicker;
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeImagePicker","HTNativeImagePicker","asyncShowImagePicker"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,iBAAiB,MAAM,wBAAqB;AAEnD,MAAMC,mBAAmB,GAAG,CAAC,MAAM;EACjC,OAAO;IACLC,oBAAoB,EAAEF,iBAAiB,CAACE;EAC1C,CAAC;AACH,CAAC,EAAE,CAAC;AAEJ,eAAeD,mBAAmB","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { type TurboModule } from 'react-native';
|
|
2
|
+
export type ImagePickerOption = {
|
|
3
|
+
imageCount: number;
|
|
4
|
+
isRecordSelected: boolean;
|
|
5
|
+
isCamera: boolean;
|
|
6
|
+
isCrop: boolean;
|
|
7
|
+
CropW: number;
|
|
8
|
+
CropH: number;
|
|
9
|
+
isGif: boolean;
|
|
10
|
+
showCropCircle: boolean;
|
|
11
|
+
circleCropRadius: number;
|
|
12
|
+
showCropFrame: boolean;
|
|
13
|
+
showCropGrid: boolean;
|
|
14
|
+
freeStyleCropEnabled: boolean;
|
|
15
|
+
rotateEnabled: boolean;
|
|
16
|
+
scaleEnabled: boolean;
|
|
17
|
+
compress: boolean;
|
|
18
|
+
compressFocusAlpha: boolean;
|
|
19
|
+
minimumCompressSize: number;
|
|
20
|
+
quality: number;
|
|
21
|
+
enableBase64: boolean;
|
|
22
|
+
allowPickingOriginalPhoto: boolean;
|
|
23
|
+
allowPickingMultipleVideo: boolean;
|
|
24
|
+
videoMaximumDuration: number;
|
|
25
|
+
isWeChatStyle: boolean;
|
|
26
|
+
sortAscendingByModificationDate: boolean;
|
|
27
|
+
videoCount: number;
|
|
28
|
+
MaxSecond: number;
|
|
29
|
+
MinSecond: number;
|
|
30
|
+
showSelectedIndex: boolean;
|
|
31
|
+
};
|
|
32
|
+
export interface SelectedPhoto {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
uri: string;
|
|
36
|
+
original_uri: string;
|
|
37
|
+
type: string;
|
|
38
|
+
size: number;
|
|
39
|
+
base64: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 默认参数
|
|
43
|
+
*/
|
|
44
|
+
export declare const defaultOptions: {
|
|
45
|
+
imageCount: number;
|
|
46
|
+
isRecordSelected: boolean;
|
|
47
|
+
isCamera: boolean;
|
|
48
|
+
isCrop: boolean;
|
|
49
|
+
CropW: number;
|
|
50
|
+
CropH: number;
|
|
51
|
+
isGif: boolean;
|
|
52
|
+
showCropCircle: boolean;
|
|
53
|
+
circleCropRadius: number;
|
|
54
|
+
showCropFrame: boolean;
|
|
55
|
+
showCropGrid: boolean;
|
|
56
|
+
freeStyleCropEnabled: boolean;
|
|
57
|
+
rotateEnabled: boolean;
|
|
58
|
+
scaleEnabled: boolean;
|
|
59
|
+
compress: boolean;
|
|
60
|
+
compressFocusAlpha: boolean;
|
|
61
|
+
minimumCompressSize: number;
|
|
62
|
+
quality: number;
|
|
63
|
+
enableBase64: boolean;
|
|
64
|
+
allowPickingOriginalPhoto: boolean;
|
|
65
|
+
allowPickingMultipleVideo: boolean;
|
|
66
|
+
videoMaximumDuration: number;
|
|
67
|
+
isWeChatStyle: boolean;
|
|
68
|
+
sortAscendingByModificationDate: boolean;
|
|
69
|
+
showSelectedIndex: boolean;
|
|
70
|
+
};
|
|
71
|
+
export interface Spec extends TurboModule {
|
|
72
|
+
asyncShowImagePicker(options: ImagePickerOption): Promise<Array<SelectedPhoto>>;
|
|
73
|
+
}
|
|
74
|
+
declare const _default: Spec;
|
|
75
|
+
export default _default;
|
|
76
|
+
//# sourceMappingURL=NativeImagePicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NativeImagePicker.d.ts","sourceRoot":"","sources":["../../../src/NativeImagePicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,MAAM,iBAAiB,GAAG;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,kBAAkB,EAAC,OAAO,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,OAAO,CAAC;IACtB,yBAAyB,EAAE,OAAO,CAAC;IACnC,yBAAyB,EAAE,OAAO,CAAC;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,OAAO,CAAC;IACvB,+BAA+B,EAAE,OAAO,CAAA;IACxC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAA;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAC,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAC,MAAM,CAAC;IACZ,MAAM,EAAC,MAAM,CAAA;CACd;AAGH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;CA0B1B,CAAC;AACF,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,oBAAoB,CAAC,OAAO,EAAC,iBAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;CAChF;;AAED,wBAA2E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,QAAA,MAAM,mBAAmB;;CAIrB,CAAA;AAEJ,eAAe,mBAAmB,CAAA"}
|