[Image] Add onLoadStart, onLoadProgress, onLoadError events to Image component

Summary:
This PR adds 4 native events to NetworkImage.

![demo](http://zippy.gfycat.com/MelodicLawfulCaecilian.gif)

Using these events I could wrap `Image` component into something like:
```javascript
class NetworkImage extends React.Component {
  getInitialState() {
    return {
      downloading: false,
      progress: 0
    }
  }

  render() {
    var loader = this.state.downloading ?
      <View style={this.props.loaderStyles}>
        <ActivityIndicatorIOS animating={true} size={'large'} />
        <Text style={{color: '#bbb'}}>{this.state.progress}%</Text>
      </View>
      :
      null;

    return <Image source={this.props.source}
      onLoadStart={() => this.setState({downloading: true}) }
      onLoaded={() => this.setState({downloading: false}) }
      onLoadProgress={(e)=> this.setState({progress: Math.round(100 * e.nativeEvent.written / e.nativeEvent.total)});
      onLoadError={(e)=> {
        alert('the image cannot be downloaded because: ', JSON.stringify(e));
        this.setState({downloading: false});
      }}>
      {loader}
    </Image>
  }
}
```
Useful on slow connections and server errors.

There are dozen lines of Objective C, which I don't have experience with. There are neither specific tests nor documentation yet. And I do realize that you're already working right now on better `<Image/>` (pipeline, new asset management, etc.). So this is basically a proof concept of events for images, and if this idea is not completely wrong I could improve it or help somehow.

Closes https://github.com/facebook/react-native/pull/1318
Github Author: Dmitriy Loktev <unknownliveid@hotmail.com>
This commit is contained in:
Dmitriy Loktev 2015-07-09 15:48:22 -01:00
Родитель 54c21ac651
Коммит 8e70c7f003
10 изменённых файлов: 217 добавлений и 24 удалений

Просмотреть файл

@ -105,6 +105,32 @@ var Image = React.createClass({
* {nativeEvent: { layout: {x, y, width, height}}}. * {nativeEvent: { layout: {x, y, width, height}}}.
*/ */
onLayout: PropTypes.func, onLayout: PropTypes.func,
/**
* Invoked on load start
*/
onLoadStart: PropTypes.func,
/**
* Invoked on download progress with
*
* {nativeEvent: { written, total}}.
*/
onLoadProgress: PropTypes.func,
/**
* Invoked on load abort
*/
onLoadAbort: PropTypes.func,
/**
* Invoked on load error
*
* {nativeEvent: { error}}.
*/
onLoadError: PropTypes.func,
/**
* Invoked on load end
*
*/
onLoaded: PropTypes.func
}, },
statics: { statics: {
@ -161,6 +187,7 @@ var Image = React.createClass({
if (this.props.defaultSource) { if (this.props.defaultSource) {
nativeProps.defaultImageSrc = this.props.defaultSource.uri; nativeProps.defaultImageSrc = this.props.defaultSource.uri;
} }
nativeProps.progressHandlerRegistered = isNetwork && this.props.onLoadProgress;
return <RawImage {...nativeProps} />; return <RawImage {...nativeProps} />;
} }
}); });
@ -178,6 +205,7 @@ var nativeOnlyProps = {
src: true, src: true,
defaultImageSrc: true, defaultImageSrc: true,
imageTag: true, imageTag: true,
progressHandlerRegistered: true
}; };
if (__DEV__) { if (__DEV__) {
verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps); verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps);

Просмотреть файл

@ -0,0 +1,26 @@
/**
* 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 <Foundation/Foundation.h>
typedef void (^RCTDataCompletionBlock)(NSURLResponse *response, NSData *data, NSError *error);
typedef void (^RCTDataProgressBlock)(int64_t written, int64_t total);
@interface RCTDownloadTaskWrapper : NSObject <NSURLSessionDownloadDelegate>
@property (copy, nonatomic) RCTDataCompletionBlock completionBlock;
@property (copy, nonatomic) RCTDataProgressBlock progressBlock;
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue;
- (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock;
@property (nonatomic, assign) id<NSURLSessionDownloadDelegate> delegate;
@end

Просмотреть файл

@ -0,0 +1,69 @@
/**
* 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 "RCTDownloadTaskWrapper.h"
@implementation RCTDownloadTaskWrapper
{
NSURLSession *_URLSession;
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue
{
if ((self = [super init])) {
_URLSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
}
return self;
}
- (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock
{
self.completionBlock = completionBlock;
self.progressBlock = progressBlock;
NSURLSessionDownloadTask *task = [_URLSession downloadTaskWithURL:url completionHandler:nil];
[task resume];
return task;
}
#pragma mark - NSURLSessionTaskDelegate methods
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
if (self.completionBlock) {
NSData *data = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
self.completionBlock(downloadTask.response, data, nil);
});
}
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)didWriteData totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
{
dispatch_async(dispatch_get_main_queue(), ^{
if (self.progressBlock != nil) {
self.progressBlock(totalBytesWritten, totalBytesExpectedToWrite);
}
});
}
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
if (error && self.completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
self.completionBlock(NULL, NULL, error);
});
}
}
@end

Просмотреть файл

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */; };
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */; }; 1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */; };
1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; }; 1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; };
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; }; 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
@ -32,6 +33,8 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTaskWrapper.h; sourceTree = "<group>"; };
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTaskWrapper.m; sourceTree = "<group>"; };
1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = "<group>"; }; 1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = "<group>"; };
1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = "<group>"; }; 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = "<group>"; };
1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = "<group>"; }; 1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = "<group>"; };
@ -71,6 +74,8 @@
children = ( children = (
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */, 143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */, 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */,
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */,
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */, 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */,
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */, 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */,
58B511891A9E6BD600147676 /* RCTImageDownloader.h */, 58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
@ -168,6 +173,7 @@
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */, 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */,
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */, 143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */, 143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */,
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */, 1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

Просмотреть файл

@ -9,6 +9,8 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "RCTDownloadTaskWrapper.h"
typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error); typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error);
typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error); typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
@ -22,6 +24,7 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
* the main thread. Returns a token that can be used to cancel the download. * the main thread. Returns a token that can be used to cancel the download.
*/ */
- (id)downloadDataForURL:(NSURL *)url - (id)downloadDataForURL:(NSURL *)url
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTDataDownloadBlock)block; block:(RCTDataDownloadBlock)block;
/** /**
@ -35,6 +38,7 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
scale:(CGFloat)scale scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode resizeMode:(UIViewContentMode)resizeMode
backgroundColor:(UIColor *)backgroundColor backgroundColor:(UIColor *)backgroundColor
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block; block:(RCTImageDownloadBlock)block;
/** /**

Просмотреть файл

@ -9,6 +9,7 @@
#import "RCTImageDownloader.h" #import "RCTImageDownloader.h"
#import "RCTDownloadTaskWrapper.h"
#import "RCTLog.h" #import "RCTLog.h"
#import "RCTUtils.h" #import "RCTUtils.h"
@ -45,12 +46,15 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
return self; return self;
} }
- (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block - (id)_downloadDataForURL:(NSURL *)url progressBlock:progressBlock block:(RCTCachedDataDownloadBlock)block
{ {
NSString *cacheKey = url.absoluteString; NSString *cacheKey = url.absoluteString;
__block BOOL cancelled = NO; __block BOOL cancelled = NO;
__block NSURLSessionDataTask *task = nil; __block NSURLSessionDownloadTask *task = nil;
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
RCTDownloadTaskWrapper *downloadTaskWrapper = [[RCTDownloadTaskWrapper alloc] initWithSessionConfiguration:config delegateQueue:nil];
dispatch_block_t cancel = ^{ dispatch_block_t cancel = ^{
cancelled = YES; cancelled = YES;
@ -85,7 +89,8 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
}); });
}; };
task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSURLRequest *request = [NSURLRequest requestWithURL:url];
task = [downloadTaskWrapper downloadData:url progressBlock:progressBlock completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!cancelled) { if (!cancelled) {
runBlocks(NO, data, error); runBlocks(NO, data, error);
} }
@ -93,12 +98,12 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
if (response) { if (response) {
RCTImageDownloader *strongSelf = weakSelf; RCTImageDownloader *strongSelf = weakSelf;
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed]; NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[strongSelf->_cache storeCachedResponse:cachedResponse forDataTask:task]; [strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
} }
task = nil; task = nil;
}]; }];
[_cache getCachedResponseForDataTask:task completionHandler:^(NSCachedURLResponse *cachedResponse) { NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
if (cancelled) { if (cancelled) {
return; return;
} }
@ -108,16 +113,16 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
} else { } else {
[task resume]; [task resume];
} }
}];
} }
}); });
return [cancel copy]; return [cancel copy];
} }
- (id)downloadDataForURL:(NSURL *)url block:(RCTDataDownloadBlock)block - (id)downloadDataForURL:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock block:(RCTDataDownloadBlock)block
{ {
return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) { return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSData *data, NSError *error) {
block(data, error); block(data, error);
}]; }];
} }
@ -127,9 +132,10 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
scale:(CGFloat)scale scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode resizeMode:(UIViewContentMode)resizeMode
backgroundColor:(UIColor *)backgroundColor backgroundColor:(UIColor *)backgroundColor
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block block:(RCTImageDownloadBlock)block
{ {
return [self downloadDataForURL:url block:^(NSData *data, NSError *error) { return [self downloadDataForURL:url progressBlock:progressBlock block:^(NSData *data, NSError *error) {
if (!data || error) { if (!data || error) {
block(nil, error); block(nil, error);
return; return;

Просмотреть файл

@ -121,7 +121,7 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil); RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil);
return; return;
} }
[[RCTImageDownloader sharedInstance] downloadDataForURL:url block:^(NSData *data, NSError *error) { [[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:nil block:^(NSData *data, NSError *error) {
if (error) { if (error) {
RCTDispatchCallbackOnMainQueue(callback, error, nil); RCTDispatchCallbackOnMainQueue(callback, error, nil);
} else { } else {

Просмотреть файл

@ -9,11 +9,13 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@class RCTEventDispatcher;
@class RCTImageDownloader; @class RCTImageDownloader;
@interface RCTNetworkImageView : UIView @interface RCTNetworkImageView : UIView
- (instancetype)initWithImageDownloader:(RCTImageDownloader *)imageDownloader NS_DESIGNATED_INITIALIZER; - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
imageDownloader:(RCTImageDownloader *)imageDownloader NS_DESIGNATED_INITIALIZER;
/** /**
* An image that will appear while the view is loading the image from the network, * An image that will appear while the view is loading the image from the network,

Просмотреть файл

@ -14,23 +14,27 @@
#import "RCTGIFImage.h" #import "RCTGIFImage.h"
#import "RCTImageDownloader.h" #import "RCTImageDownloader.h"
#import "RCTUtils.h" #import "RCTUtils.h"
#import "RCTBridgeModule.h"
#import "RCTEventDispatcher.h"
#import "UIView+React.h" #import "UIView+React.h"
@implementation RCTNetworkImageView @implementation RCTNetworkImageView
{ {
BOOL _deferred; BOOL _deferred;
BOOL _progressHandlerRegistered;
NSURL *_imageURL; NSURL *_imageURL;
NSURL *_deferredImageURL; NSURL *_deferredImageURL;
NSUInteger _deferSentinel; NSUInteger _deferSentinel;
RCTImageDownloader *_imageDownloader; RCTImageDownloader *_imageDownloader;
id _downloadToken; id _downloadToken;
RCTEventDispatcher *_eventDispatcher;
} }
- (instancetype)initWithImageDownloader:(RCTImageDownloader *)imageDownloader - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher imageDownloader:(RCTImageDownloader *)imageDownloader
{ {
RCTAssertParam(imageDownloader);
if ((self = [super initWithFrame:CGRectZero])) { if ((self = [super initWithFrame:CGRectZero])) {
_eventDispatcher = eventDispatcher;
_progressHandlerRegistered = NO;
_deferSentinel = 0; _deferSentinel = 0;
_imageDownloader = imageDownloader; _imageDownloader = imageDownloader;
self.userInteractionEnabled = NO; self.userInteractionEnabled = NO;
@ -56,6 +60,11 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
[self _updateImage]; [self _updateImage];
} }
- (void)setProgressHandlerRegistered:(BOOL)progressHandlerRegistered
{
_progressHandlerRegistered = progressHandlerRegistered;
}
- (void)reactSetFrame:(CGRect)frame - (void)reactSetFrame:(CGRect)frame
{ {
[super reactSetFrame:frame]; [super reactSetFrame:frame];
@ -89,8 +98,34 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
self.layer.minificationFilter = kCAFilterTrilinear; self.layer.minificationFilter = kCAFilterTrilinear;
self.layer.magnificationFilter = kCAFilterTrilinear; self.layer.magnificationFilter = kCAFilterTrilinear;
} }
[_eventDispatcher sendInputEventWithName:@"loadStart" body:@{ @"target": self.reactTag }];
RCTDataProgressBlock progressHandler = ^(int64_t written, int64_t total) {
if (_progressHandlerRegistered) {
NSDictionary *event = @{
@"target": self.reactTag,
@"written": @(written),
@"total": @(total),
};
[_eventDispatcher sendInputEventWithName:@"loadProgress" body:event];
}
};
void (^errorHandler)(NSString *errorDescription) = ^(NSString *errorDescription) {
NSDictionary *event = @{
@"target": self.reactTag,
@"error": errorDescription,
};
[_eventDispatcher sendInputEventWithName:@"loadError" body:event];
};
void (^loadEndHandler)(void) = ^(void) {
NSDictionary *event = @{ @"target": self.reactTag };
[_eventDispatcher sendInputEventWithName:@"loaded" body:event];
};
if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) { if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
_downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) { _downloadToken = [_imageDownloader downloadDataForURL:imageURL progressBlock:progressHandler block:^(NSData *data, NSError *error) {
if (data) { if (data) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (imageURL != self.imageURL) { if (imageURL != self.imageURL) {
@ -102,13 +137,16 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
self.layer.minificationFilter = kCAFilterLinear; self.layer.minificationFilter = kCAFilterLinear;
self.layer.magnificationFilter = kCAFilterLinear; self.layer.magnificationFilter = kCAFilterLinear;
[self.layer addAnimation:animation forKey:@"contents"]; [self.layer addAnimation:animation forKey:@"contents"];
loadEndHandler();
}); });
} else if (error) { } else if (error) {
RCTLogWarn(@"Unable to download image data. Error: %@", error); errorHandler([error description]);
} }
}]; }];
} else { } else {
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() resizeMode:self.contentMode backgroundColor:self.backgroundColor block:^(UIImage *image, NSError *error) { _downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale()
resizeMode:self.contentMode backgroundColor:self.backgroundColor
progressBlock:progressHandler block:^(UIImage *image, NSError *error) {
if (image) { if (image) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (imageURL != self.imageURL) { if (imageURL != self.imageURL) {
@ -118,9 +156,10 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
[self.layer removeAnimationForKey:@"contents"]; [self.layer removeAnimationForKey:@"contents"];
self.layer.contentsScale = image.scale; self.layer.contentsScale = image.scale;
self.layer.contents = (__bridge id)image.CGImage; self.layer.contents = (__bridge id)image.CGImage;
loadEndHandler();
}); });
} else if (error) { } else if (error) {
RCTLogWarn(@"Unable to download image. Error: %@", error); errorHandler([error description]);
} }
}]; }];
} }

Просмотреть файл

@ -9,24 +9,37 @@
#import "RCTNetworkImageViewManager.h" #import "RCTNetworkImageViewManager.h"
#import "RCTNetworkImageView.h" #import "RCTBridge.h"
#import "RCTConvert.h" #import "RCTConvert.h"
#import "RCTUtils.h"
#import "RCTImageDownloader.h" #import "RCTImageDownloader.h"
#import "RCTNetworkImageView.h"
#import "RCTUtils.h"
@implementation RCTNetworkImageViewManager @implementation RCTNetworkImageViewManager
RCT_EXPORT_MODULE() RCT_EXPORT_MODULE()
@synthesize bridge = _bridge;
- (UIView *)view - (UIView *)view
{ {
return [[RCTNetworkImageView alloc] initWithImageDownloader:[RCTImageDownloader sharedInstance]]; return [[RCTNetworkImageView alloc] initWithEventDispatcher:self.bridge.eventDispatcher imageDownloader:[RCTImageDownloader sharedInstance]];
} }
RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage) RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage)
RCT_REMAP_VIEW_PROPERTY(src, imageURL, NSURL) RCT_REMAP_VIEW_PROPERTY(src, imageURL, NSURL)
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode) RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
RCT_EXPORT_VIEW_PROPERTY(progressHandlerRegistered, BOOL)
- (NSDictionary *)customDirectEventTypes
{
return @{
@"loadStart": @{ @"registrationName": @"onLoadStart" },
@"loadProgress": @{ @"registrationName": @"onLoadProgress" },
@"loaded": @{ @"registrationName": @"onLoaded" },
@"loadError": @{ @"registrationName": @"onLoadError" },
@"loadAbort": @{ @"registrationName": @"onLoadAbort" },
};
}
@end @end