Add partial image loading to RCTImageView

Reviewed By: javache

Differential Revision: D3856918

fbshipit-source-id: ca98f8604213e7e583a188ccc4c25ea9d7aa9aa2
This commit is contained in:
Mehdi Mulani 2016-09-21 12:11:19 -07:00 committed by Facebook Github Bot 0
parent 048449b678
commit 820b1c0e46
9 changed files with 55 additions and 14 deletions

View File

@ -47,7 +47,7 @@
return _canLoadImageURLHandler(requestURL);
}
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(RCTResizeMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler);
}

View File

@ -61,7 +61,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
[bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
} partialLoadBlock:nil completionBlock:^(NSError *loadError, id loadedImage) {
XCTAssertEqualObjects(loadedImage, image);
XCTAssertNil(loadError);
}];
@ -92,7 +92,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
[bridge.imageLoader loadImageWithURLRequest:urlRequest size:CGSizeMake(100, 100) scale:1.0 clipped:YES resizeMode:RCTResizeModeContain progressBlock:^(int64_t progress, int64_t total) {
XCTAssertEqual(progress, 1);
XCTAssertEqual(total, 1);
} completionBlock:^(NSError *loadError, id loadedImage) {
} partialLoadBlock:nil completionBlock:^(NSError *loadError, id loadedImage) {
XCTAssertEqualObjects(loadedImage, image);
XCTAssertNil(loadError);
}];

View File

@ -36,6 +36,7 @@ RCT_EXPORT_MODULE()
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
// Using PhotoKit for iOS 8+

View File

@ -257,6 +257,13 @@ const Image = React.createClass({
* @platform ios
*/
onError: PropTypes.func,
/**
* Invoked when a partial load of the image is complete. The definition of
* what constitutes a "partial load" is loader specific though this is meant
* for progressive JPEG loads.
* @platform ios
*/
onPartialLoad: PropTypes.func,
/**
* Invoked when load completes successfully.
*/

View File

@ -14,6 +14,7 @@
#import "RCTResizeMode.h"
typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total);
typedef void (^RCTImageLoaderPartialLoadBlock)(UIImage *image);
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, UIImage *image);
typedef dispatch_block_t RCTImageLoaderCancellationBlock;
@ -82,6 +83,9 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
* select the optimal dimensions for the loaded image. The `clipped` option
* controls whether the image will be clipped to fit the specified size exactly,
* or if the original aspect ratio should be retained.
* `partialLoadBlock` is meant for custom image loaders that do not ship with the core RN library.
* It is meant to be called repeatedly while loading the image as higher quality versions are decoded,
* for instance with progressive JPEGs.
*/
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
size:(CGSize)size
@ -89,6 +93,7 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
clipped:(BOOL)clipped
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock;
/**
@ -155,6 +160,7 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
@optional

View File

@ -229,6 +229,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
clipped:YES
resizeMode:RCTResizeModeStretch
progressBlock:nil
partialLoadBlock:nil
completionBlock:callback];
}
@ -292,6 +293,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate))completionBlock
{
{
@ -343,6 +345,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandler:^(NSError *error, UIImage *image){
completionHandler(error, image, nil);
}];
@ -366,6 +369,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
partialLoadHandler:partialLoadHandler
completionHandler:^(NSError *error, UIImage *image) {
completionHandler(error, image, nil);
}];
@ -469,7 +473,11 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
});
}];
task.downloadProgressBlock = progressHandler;
task.downloadProgressBlock = ^(int64_t progress, int64_t total) {
if (progressHandler) {
progressHandler(progress, total);
}
};
if (task) {
if (!_pendingTasks) {
@ -491,6 +499,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
clipped:(BOOL)clipped
resizeMode:(RCTResizeMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
partialLoadBlock:(RCTImageLoaderPartialLoadBlock)partialLoadBlock
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
__block volatile uint32_t cancelled = 0;
@ -557,6 +566,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
scale:scale
resizeMode:resizeMode
progressBlock:progressBlock
partialLoadBlock:partialLoadBlock
completionBlock:completionHandler];
return cancellationBlock;
}
@ -693,6 +703,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
scale:1
resizeMode:RCTResizeModeStretch
progressBlock:NULL
partialLoadBlock:NULL
completionBlock:completion];
}

View File

@ -55,6 +55,7 @@ static NSDictionary *onLoadParamsForSource(RCTImageSource *source)
@property (nonatomic, copy) RCTDirectEventBlock onLoadStart;
@property (nonatomic, copy) RCTDirectEventBlock onProgress;
@property (nonatomic, copy) RCTDirectEventBlock onError;
@property (nonatomic, copy) RCTDirectEventBlock onPartialLoad;
@property (nonatomic, copy) RCTDirectEventBlock onLoad;
@property (nonatomic, copy) RCTDirectEventBlock onLoadEnd;
@ -296,6 +297,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
};
}
__weak RCTImageView *weakSelf = self;
RCTImageLoaderPartialLoadBlock partialLoadHandler = ^(UIImage *image) {
[weakSelf imageLoaderLoadedImage:image error:nil forImageSource:source partial:YES];
};
CGSize imageSize = self.bounds.size;
CGFloat imageScale = RCTScreenScale();
if (!UIEdgeInsetsEqualToEdgeInsets(_capInsets, UIEdgeInsetsZero)) {
@ -304,9 +310,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
imageScale = source.scale;
}
__weak RCTImageView *weakSelf = self;
RCTImageLoaderCompletionBlock completionHandler = ^(NSError *error, UIImage *loadedImage) {
[weakSelf imageLoaderLoadedImage:loadedImage error:error forImageSource:source];
[weakSelf imageLoaderLoadedImage:loadedImage error:error forImageSource:source partial:NO];
};
_reloadImageCancellationBlock =
@ -316,13 +321,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
clipped:NO
resizeMode:_resizeMode
progressBlock:progressHandler
partialLoadBlock:partialLoadHandler
completionBlock:completionHandler];
} else {
[self clearImage];
}
}
- (void)imageLoaderLoadedImage:(UIImage *)loadedImage error:(NSError *)error forImageSource:(RCTImageSource *)source
- (void)imageLoaderLoadedImage:(UIImage *)loadedImage error:(NSError *)error forImageSource:(RCTImageSource *)source partial:(BOOL)isPartialLoad
{
if (![source isEqual:_pendingImageSource]) {
// Bail out if source has changed since we started loading
@ -340,8 +346,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
}
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
self->_imageSource = source;
self->_pendingImageSource = nil;
if (!isPartialLoad) {
self->_imageSource = source;
self->_pendingImageSource = nil;
}
if (image.reactKeyframeAnimation) {
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
@ -350,11 +358,17 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
self.image = image;
}
if (self->_onLoad) {
self->_onLoad(onLoadParamsForSource(source));
}
if (self->_onLoadEnd) {
self->_onLoadEnd(nil);
if (isPartialLoad) {
if (self->_onPartialLoad) {
self->_onPartialLoad(nil);
}
} else {
if (self->_onLoad) {
self->_onLoad(onLoadParamsForSource(source));
}
if (self->_onLoadEnd) {
self->_onLoadEnd(nil);
}
}
};

View File

@ -31,6 +31,7 @@ RCT_REMAP_VIEW_PROPERTY(defaultSource, defaultImage, UIImage)
RCT_EXPORT_VIEW_PROPERTY(onLoadStart, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPartialLoad, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(resizeMode, RCTResizeMode)

View File

@ -71,6 +71,7 @@ static NSBundle *bundleForPath(NSString *key)
scale:(CGFloat)scale
resizeMode:(RCTResizeMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
partialLoadHandler:(RCTImageLoaderPartialLoadBlock)partialLoadHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
__block volatile uint32_t cancelled = 0;