mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-03-26 07:04:05 +08:00
[React Native] open source ImageStoreManager native module and plug into RCTImageLoader
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
@@ -22,11 +23,13 @@
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_METHOD(saveImageWithTag:(NSString *)imageTag
|
||||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
errorCallback:(RCTResponseErrorBlock)errorCallback)
|
||||
{
|
||||
[RCTImageLoader loadImageWithTag:imageTag callback:^(NSError *loadError, UIImage *loadedImage) {
|
||||
[RCTImageLoader loadImageWithTag:imageTag bridge:_bridge callback:^(NSError *loadError, UIImage *loadedImage) {
|
||||
if (loadError) {
|
||||
errorCallback(loadError);
|
||||
return;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; };
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
|
||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -50,6 +51,8 @@
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = "<group>"; };
|
||||
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = "<group>"; };
|
||||
143879371AAD32A300F088A5 /* RCTImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoader.m; sourceTree = "<group>"; };
|
||||
35123E691B59C99D00EBAD80 /* RCTImageStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageStoreManager.h; sourceTree = "<group>"; };
|
||||
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageStoreManager.m; sourceTree = "<group>"; };
|
||||
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
|
||||
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
|
||||
@@ -87,6 +90,8 @@
|
||||
1304D5A81AA8C4A30002E2BE /* RCTImageView.m */,
|
||||
1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */,
|
||||
1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */,
|
||||
35123E691B59C99D00EBAD80 /* RCTImageStoreManager.h */,
|
||||
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */,
|
||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
|
||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
|
||||
58B5115E1A9E6B3D00147676 /* Products */,
|
||||
@@ -159,6 +164,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
|
||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
|
||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */,
|
||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class ALAssetsLibrary;
|
||||
@class RCTBridge;
|
||||
|
||||
typedef void (^RCTImageLoaderProgressBlock)(int64_t written, int64_t total);
|
||||
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id /* UIImage or CAAnimation */);
|
||||
@@ -27,6 +28,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
||||
* Will always call callback on main thread.
|
||||
*/
|
||||
+ (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
|
||||
bridge:(RCTBridge *)bridge
|
||||
callback:(RCTImageLoaderCompletionBlock)callback;
|
||||
|
||||
/**
|
||||
@@ -37,6 +39,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
bridge:(RCTBridge *)bridge
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progress
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completion;
|
||||
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
#import <Photos/PHImageManager.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTGIFImage.h"
|
||||
#import "RCTImageDownloader.h"
|
||||
#import "RCTImageStoreManager.h"
|
||||
#import "RCTImageUtils.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
@@ -58,12 +60,14 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
||||
}
|
||||
|
||||
+ (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
|
||||
bridge:(RCTBridge *)bridge
|
||||
callback:(RCTImageLoaderCompletionBlock)callback
|
||||
{
|
||||
return [self loadImageWithTag:imageTag
|
||||
size:CGSizeZero
|
||||
scale:0
|
||||
resizeMode:UIViewContentModeScaleToFill
|
||||
bridge:bridge
|
||||
progressBlock:nil
|
||||
completionBlock:callback];
|
||||
}
|
||||
@@ -72,6 +76,7 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
bridge:(RCTBridge *)bridge
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progress
|
||||
completionBlock:(RCTImageLoaderCompletionBlock)completion
|
||||
{
|
||||
@@ -177,6 +182,17 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, image);
|
||||
}];
|
||||
}
|
||||
} else if ([imageTag hasPrefix:@"rct-image-store://"]) {
|
||||
[bridge.imageStoreManager getImageForTag:imageTag withBlock:^(UIImage *image) {
|
||||
if (image) {
|
||||
RCTDispatchCallbackOnMainQueue(completion, nil, image);
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load image from image store: %@", imageTag];
|
||||
NSError *error = RCTErrorWithMessage(errorMessage);
|
||||
RCTDispatchCallbackOnMainQueue(completion, error, nil);
|
||||
}
|
||||
}];
|
||||
return ^{};
|
||||
} else if ([imageTag.lowercaseString hasSuffix:@".gif"]) {
|
||||
id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
|
||||
if (image) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@@ -20,6 +21,8 @@
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||
{
|
||||
return [@[@"assets-library", @"ph"] containsObject:[request.URL.scheme lowercaseString]];
|
||||
@@ -30,7 +33,7 @@ RCT_EXPORT_MODULE()
|
||||
{
|
||||
NSNumber *requestToken = @(++_currentToken);
|
||||
NSString *URLString = [request.URL absoluteString];
|
||||
[RCTImageLoader loadImageWithTag:URLString callback:^(NSError *error, UIImage *image) {
|
||||
[RCTImageLoader loadImageWithTag:URLString bridge:_bridge callback:^(NSError *error, UIImage *image) {
|
||||
if (error) {
|
||||
[delegate URLRequest:requestToken didCompleteWithError:error];
|
||||
return;
|
||||
|
||||
29
Libraries/Image/RCTImageStoreManager.h
Normal file
29
Libraries/Image/RCTImageStoreManager.h
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTURLRequestHandler.h"
|
||||
|
||||
@interface RCTImageStoreManager : NSObject<RCTURLRequestHandler>
|
||||
|
||||
/**
|
||||
* Set and get cached images. These must be called from the main thread.
|
||||
*/
|
||||
- (NSString *)storeImage:(UIImage *)image;
|
||||
- (UIImage *)imageForTag:(NSString *)imageTag;
|
||||
|
||||
/**
|
||||
* Set and get cached images asynchronously. It is safe to call these from any
|
||||
* thread. The callbacks will be called on the main thread.
|
||||
*/
|
||||
- (void)storeImage:(UIImage *)image withBlock:(void (^)(NSString *imageTag))block;
|
||||
- (void)getImageForTag:(NSString *)imageTag withBlock:(void (^)(UIImage *image))block;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTImageStoreManager)
|
||||
|
||||
@property (nonatomic, readonly) RCTImageStoreManager *imageStoreManager;
|
||||
|
||||
@end
|
||||
149
Libraries/Image/RCTImageStoreManager.m
Normal file
149
Libraries/Image/RCTImageStoreManager.m
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTImageStoreManager.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTImageStoreManager
|
||||
{
|
||||
NSMutableDictionary *_store;
|
||||
}
|
||||
|
||||
@synthesize methodQueue = _methodQueue;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
// TODO: need a way to clear this store
|
||||
_store = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)storeImage:(UIImage *)image
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
NSString *tag = [NSString stringWithFormat:@"rct-image-store://%tu", [_store count]];
|
||||
_store[tag] = image;
|
||||
return tag;
|
||||
}
|
||||
|
||||
- (UIImage *)imageForTag:(NSString *)imageTag
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
return _store[imageTag];
|
||||
}
|
||||
|
||||
- (void)storeImage:(UIImage *)image withBlock:(void (^)(NSString *imageTag))block
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSString *imageTag = [self storeImage:image];
|
||||
if (block) {
|
||||
block(imageTag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)getImageForTag:(NSString *)imageTag withBlock:(void (^)(UIImage *image))block
|
||||
{
|
||||
RCTAssert(block != nil, @"block must not be nil");
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block([self imageForTag:imageTag]);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO (#5906496): Name could be more explicit - something like getBase64EncodedJPEGDataForTag:?
|
||||
RCT_EXPORT_METHOD(getBase64ForTag:(NSString *)imageTag
|
||||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
errorCallback:(RCTResponseErrorBlock)errorCallback)
|
||||
{
|
||||
[self getImageForTag:imageTag withBlock:^(UIImage *image) {
|
||||
if (!image) {
|
||||
errorCallback(RCTErrorWithMessage([NSString stringWithFormat:@"Invalid imageTag: %@", imageTag]));
|
||||
return;
|
||||
}
|
||||
dispatch_async(_methodQueue, ^{
|
||||
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
|
||||
NSString *base64 = [imageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
|
||||
successCallback(@[[base64 stringByReplacingOccurrencesOfString:@"\n" withString:@""]]);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String
|
||||
successCallback:(RCTResponseSenderBlock)successCallback
|
||||
errorCallback:(RCTResponseErrorBlock)errorCallback)
|
||||
|
||||
{
|
||||
NSData *imageData = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
|
||||
if (imageData) {
|
||||
UIImage *image = [[UIImage alloc] initWithData:imageData];
|
||||
[self storeImage:image withBlock:^(NSString *imageTag) {
|
||||
successCallback(@[imageTag]);
|
||||
}];
|
||||
} else {
|
||||
errorCallback(RCTErrorWithMessage(@"Failed to add image from base64String"));
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RCTURLRequestHandler
|
||||
|
||||
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
||||
{
|
||||
return [@[@"rct-image-store"] containsObject:[request.URL.scheme lowercaseString]];
|
||||
}
|
||||
|
||||
- (id)sendRequest:(NSURLRequest *)request
|
||||
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
||||
{
|
||||
NSString *imageTag = [request.URL absoluteString];
|
||||
[self getImageForTag:imageTag withBlock:^(UIImage *image) {
|
||||
if (!image) {
|
||||
NSError *error = RCTErrorWithMessage([NSString stringWithFormat:@"Invalid imageTag: %@", imageTag]);
|
||||
[delegate URLRequest:request didCompleteWithError:error];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *mimeType = nil;
|
||||
NSData *imageData = nil;
|
||||
if (RCTImageHasAlpha(image.CGImage)) {
|
||||
mimeType = @"image/png";
|
||||
imageData = UIImagePNGRepresentation(image);
|
||||
} else {
|
||||
mimeType = @"image/jpeg";
|
||||
imageData = UIImageJPEGRepresentation(image, 1.0);
|
||||
}
|
||||
|
||||
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
|
||||
MIMEType:mimeType
|
||||
expectedContentLength:imageData.length
|
||||
textEncodingName:nil];
|
||||
|
||||
[delegate URLRequest:request didReceiveResponse:response];
|
||||
[delegate URLRequest:request didReceiveData:imageData];
|
||||
[delegate URLRequest:request didCompleteWithError:nil];
|
||||
}];
|
||||
return request;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridge (RCTImageStoreManager)
|
||||
|
||||
- (RCTImageStoreManager *)imageStoreManager
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTImageStoreManager class])];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -124,6 +124,7 @@ RCT_NOT_IMPLEMENTED(-init)
|
||||
size:self.bounds.size
|
||||
scale:RCTScreenScale()
|
||||
resizeMode:self.contentMode
|
||||
bridge:_bridge
|
||||
progressBlock:progressHandler
|
||||
completionBlock:^(NSError *error, id image) {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user