feat(iOS): added getPhotoThumbnail (#536)

This commit is contained in:
jp-23
2023-09-29 10:49:28 +02:00
committed by GitHub
parent 7d6563905d
commit 93b1be88e0
4 changed files with 172 additions and 0 deletions
+77
View File
@@ -152,6 +152,7 @@ async function savePicture() {
* [`deletePhotos`](#deletephotos)
* [`iosGetImageDataById`](#iosgetimagedatabyid)
* [`useCameraRoll`](#usecameraroll)
* [`getPhotoThumbnail`](#getphotothumbnail) **iOS only**
---
@@ -527,6 +528,82 @@ function Example() {
};
```
### `getPhotoThumbnail()`
**iOS only**
Returns a Promise with thumbnail photo.
**Parameters:**
| Name | Type | Required | Description |
| ------------ | --------------------- | -------- | -------------------------------------------------- |
| internalID | string | Yes | Ios internal ID 'PH://xxxx'. |
| options | PhotoThumbnailOptions | Yes | Expects an options with the shape described below. |
* `allowNetworkAccess` : {boolean} : **default = false** : Specifies whether the requested image can be downloaded from iCloud. **iOS only**
* `targetSize` : {ThumbnailSize} : Expects a targetSize with the shape desribed below:
* `height` : {number} : **default = 400**
* `width` : {number} : **default = 400**
* `quality` : {number} : **default = 1.0** : jpeg quality used for compression (a value from 0.0 to 1.0). A value of 0.0 is maximum compression (or lowest quality). A value of 1.0 is least compression (or best quality).
**Returns:**
| Type | Description |
| ------------------------- | ------------------------------------------------------------- |
| Promise\<PhotoThumbnail\> | A Promise with PhotoThumbnail with the shape described below. |
* `thumbnailBase64` : {string}
#### Example
Loading a thumbnail:
```javascript
export default function Thumbnail(props) {
const [base64Image, setBase64Image] = useState(null);
useEffect(() => {
const getThumbnail = async () => {
const options = {
allowNetworkAccess: true,
targetSize: {
height: 80,
width: 80
},
quality: 1.0
};
const thumbnailResponse = await CameraRoll.getPhotoThumbnail(props.image.uri, options);
setBase64Image(thumbnailResponse.thumbnailBase64);
};
getThumbnail();
}, []);
const extension = props.image.extension;
let prefix;
switch (extension) {
case 'png':
prefix = 'data:image/png;base64,';
break;
default:
//all others can use jpeg
prefix = 'data:image/jpeg;base64,';
break;
}
return (
<Image
source={{ uri: `${prefix}${base64Image}` }}
/>
);
}
```
### Known issues
#### IOS
+64
View File
@@ -693,6 +693,70 @@ RCT_EXPORT_METHOD(getPhotoByInternalID:(NSString *)internalId
}, false);
}
RCT_EXPORT_METHOD(getPhotoThumbnail:(NSString *)internalId
options:(NSDictionary *)options
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject)
{
checkPhotoLibraryConfig();
BOOL const allowNetworkAccess = options[@"allowNetworkAccess"] == nil ? NO : [RCTConvert BOOL:options[@"allowNetworkAccess"]];
NSDictionary *const targetSize = [RCTConvert NSDictionary:options[@"targetSize"]];
CGFloat const targetHeight = targetSize[@"height"] == nil ? 400 : [RCTConvert CGFloat:targetSize[@"height"]];
CGFloat const targetWidth = targetSize[@"width"] == nil ? 400 : [RCTConvert CGFloat:targetSize[@"width"]];
CGFloat quality = options[@"quality"] == nil ? 1.0 : [RCTConvert CGFloat:options[@"quality"]];
requestPhotoLibraryAccess(reject, ^(bool isLimited){
PHFetchResult<PHAsset *> *fetchResult;
PHAsset *asset;
NSString *mediaIdentifier = internalId;
if ([internalId rangeOfString:@"ph://"].location != NSNotFound) {
mediaIdentifier = [internalId stringByReplacingOccurrencesOfString:@"ph://"
withString:@""];
}
fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:@[mediaIdentifier] options:nil];
if(fetchResult){
asset = fetchResult.firstObject;//only object in the array.
}
if(asset){
PHImageRequestOptions *const requestOptions = [PHImageRequestOptions new];
requestOptions.networkAccessAllowed = allowNetworkAccess;
requestOptions.version = PHImageRequestOptionsVersionUnadjusted;
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
CGSize const thumbnailSize = CGSizeMake(targetWidth, targetHeight);
[[PHImageManager defaultManager] requestImageForAsset:asset
targetSize:thumbnailSize
contentMode:PHImageContentModeAspectFill
options:requestOptions
resultHandler:^(UIImage * _Nullable image,
NSDictionary * _Nullable info) {
NSError *const error = [info objectForKey:PHImageErrorKey];
if (error) {
reject(@"Error while getting thumbnail image",@"Error while getting thumbnail image",error);
}
NSString *thumbnailBase64 = [UIImageJPEGRepresentation(image, quality) base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
resolve(@{
@"thumbnailBase64": thumbnailBase64
});
}];
} else {
NSString *errorMessage = [NSString stringWithFormat:@"Failed to load asset"
" with localIdentifier %@ with no error message.", internalId];
NSError *error = RCTErrorWithMessage(errorMessage);
reject(@"No asset found",@"No asset found",error);
}
}, false);
}
NSString *subTypeLabelForCollection(PHAssetCollection *assetCollection) {
PHAssetCollectionSubtype subtype = assetCollection.assetCollectionSubtype;
+26
View File
@@ -179,6 +179,21 @@ export type Album = {
subtype?: AlbumSubType;
};
export type ThumbnailSize = {
height: number,
width: number
};
export type PhotoThumbnailOptions = {
allowNetworkAccess: boolean, //iOS only
targetSize: ThumbnailSize,
quality: number
};
export type PhotoThumbnail = {
thumbnailBase64: string,
};
/**
* `CameraRoll` provides access to the local camera roll or photo library.
*
@@ -272,4 +287,15 @@ export class CameraRoll {
};
return RNCCameraRoll.getPhotoByInternalID(internalID, conversionOption);
}
/**
* Returns a Promise with thumbnail photo.
*
* @param internalID - PH photo internal ID.
* @param options - thumbnail photo options.
* @returns Promise<PhotoThumbnail>
*/
static getPhotoThumbnail(internalID: string, options: PhotoThumbnailOptions): Promise<PhotoThumbnail> {
return RNCCameraRoll.getPhotoThumbnail(internalID, options);
}
}
+5
View File
@@ -2,6 +2,7 @@
// we use Object type because methods on the native side use NSDictionary and ReadableMap
// and we want to stay compatible with those
import {TurboModuleRegistry, TurboModule} from 'react-native';
import type { PhotoThumbnail } from './CameraRoll';
export type AlbumSubType =
| 'AlbumRegular'
@@ -76,6 +77,10 @@ export interface Spec extends TurboModule {
internalID: string,
options: Object,
): Promise<PhotoIdentifier>;
getPhotoThumbnail(
internalID: string,
options: Object
): Promise<PhotoThumbnail>
}
export default TurboModuleRegistry.getEnforcing<Spec>('RNCCameraRoll');