[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
parent 54c21ac651
commit 8e70c7f003
10 changed files with 217 additions and 24 deletions

View File

@@ -9,6 +9,7 @@
#import "RCTImageDownloader.h"
#import "RCTDownloadTaskWrapper.h"
#import "RCTLog.h"
#import "RCTUtils.h"
@@ -45,12 +46,15 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
return self;
}
- (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block
- (id)_downloadDataForURL:(NSURL *)url progressBlock:progressBlock block:(RCTCachedDataDownloadBlock)block
{
NSString *cacheKey = url.absoluteString;
__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 = ^{
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) {
runBlocks(NO, data, error);
}
@@ -93,12 +98,12 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
if (response) {
RCTImageDownloader *strongSelf = weakSelf;
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;
}];
[_cache getCachedResponseForDataTask:task completionHandler:^(NSCachedURLResponse *cachedResponse) {
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
if (cancelled) {
return;
}
@@ -108,16 +113,16 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
} else {
[task resume];
}
}];
}
});
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);
}];
}
@@ -127,9 +132,10 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
backgroundColor:(UIColor *)backgroundColor
progressBlock:(RCTDataProgressBlock)progressBlock
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) {
block(nil, error);
return;