diff --git a/Examples/UIExplorer/ImageExample.js b/Examples/UIExplorer/ImageExample.js
index b886bf861..82721993f 100644
--- a/Examples/UIExplorer/ImageExample.js
+++ b/Examples/UIExplorer/ImageExample.js
@@ -32,7 +32,7 @@ var NetworkImageExample = React.createClass({
getInitialState: function() {
return {
error: false,
- loading: true,
+ loading: false,
progress: 0
};
},
@@ -47,10 +47,10 @@ var NetworkImageExample = React.createClass({
this.setState({error: e.nativeEvent.error})}
- onLoadProgress={(e) => this.setState({progress: Math.max(0, Math.round(100 * e.nativeEvent.written / e.nativeEvent.total))}) }
- onLoadEnd={() => this.setState({loading: false, error: false})}
- onLoadAbort={() => this.setState({error: 'Loading has aborted'})} >
+ onLoadStart={(e) => this.setState({loading: true})}
+ onError={(e) => this.setState({error: e.nativeEvent.error, loading: false})}
+ onProgress={(e) => this.setState({progress: Math.round(100 * e.nativeEvent.loaded / e.nativeEvent.total)})}
+ onLoad={() => this.setState({loading: false, error: false})}>
{loader}
;
}
diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js
index 63534af3e..e1fc6df2f 100644
--- a/Libraries/Image/Image.ios.js
+++ b/Libraries/Image/Image.ios.js
@@ -24,7 +24,6 @@ var StyleSheetPropType = require('StyleSheetPropType');
var flattenStyle = require('flattenStyle');
var invariant = require('invariant');
-var merge = require('merge');
var requireNativeComponent = require('requireNativeComponent');
var resolveAssetSource = require('resolveAssetSource');
var verifyPropTypes = require('verifyPropTypes');
@@ -57,6 +56,7 @@ var warning = require('warning');
var Image = React.createClass({
propTypes: {
+ style: StyleSheetPropType(ImageStylePropTypes),
/**
* `uri` is a string representing the resource identifier for the image, which
* could be an http address, a local file path, or the name of a static image
@@ -93,7 +93,6 @@ var Image = React.createClass({
* image dimensions.
*/
resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']),
- style: StyleSheetPropType(ImageStylePropTypes),
/**
* A unique identifier for this element to be used in UI Automation
* testing scripts.
@@ -102,7 +101,7 @@ var Image = React.createClass({
/**
* Invoked on mount and layout changes with
*
- * {nativeEvent: { layout: {x, y, width, height}}}.
+ * {nativeEvent: {layout: {x, y, width, height}}}.
*/
onLayout: PropTypes.func,
/**
@@ -112,25 +111,23 @@ var Image = React.createClass({
/**
* Invoked on download progress with
*
- * {nativeEvent: { written, total}}.
+ * {nativeEvent: {loaded, total}}.
*/
- onLoadProgress: PropTypes.func,
- /**
- * Invoked on load abort
- */
- onLoadAbort: PropTypes.func,
+ onProgress: PropTypes.func,
/**
* Invoked on load error
*
- * {nativeEvent: { error}}.
+ * {nativeEvent: {error}}.
*/
- onLoadError: PropTypes.func,
+ onError: PropTypes.func,
/**
- * Invoked on load end
- *
+ * Invoked when load completes successfully
*/
- onLoaded: PropTypes.func
-
+ onLoad: PropTypes.func,
+ /**
+ * Invoked when load either succeeds or fails
+ */
+ onLoadEnd: PropTypes.func,
},
statics: {
@@ -149,46 +146,27 @@ var Image = React.createClass({
},
render: function() {
- for (var prop in nativeOnlyProps) {
- if (this.props[prop] !== undefined) {
- console.warn('Prop `' + prop + ' = ' + this.props[prop] + '` should ' +
- 'not be set directly on Image.');
- }
- }
var source = resolveAssetSource(this.props.source) || {};
+ var defaultSource = (this.props.defaultSource && resolveAssetSource(this.props.defaultSource)) || {};
var {width, height} = source;
- var style = flattenStyle([{width, height}, styles.base, this.props.style]);
- invariant(style, 'style must be initialized');
+ var style = flattenStyle([{width, height}, styles.base, this.props.style]) || {};
var isNetwork = source.uri && source.uri.match(/^https?:/);
- invariant(
- !(isNetwork && source.isStatic),
- 'static image uris cannot start with "http": "' + source.uri + '"'
+ var RawImage = isNetwork ? RCTNetworkImageView : RCTImageView;
+ var resizeMode = this.props.resizeMode || (style || {}).resizeMode || 'cover'; // Workaround for flow bug t7737108
+ var tintColor = (style || {}).tintColor; // Workaround for flow bug t7737108
+
+ return (
+
);
- var isStored = !source.isStatic && !isNetwork;
- var RawImage = isNetwork ? RCTNetworkImage : RCTStaticImage;
-
- if (this.props.style && this.props.style.tintColor) {
- warning(RawImage === RCTStaticImage, 'tintColor style only supported on static images.');
- }
- var resizeMode = this.props.resizeMode || style.resizeMode || 'cover';
-
- var nativeProps = merge(this.props, {
- style,
- resizeMode,
- tintColor: style.tintColor,
- });
- if (isStored) {
- nativeProps.imageTag = source.uri;
- } else {
- nativeProps.src = source.uri;
- }
- if (this.props.defaultSource) {
- nativeProps.defaultImageSrc = this.props.defaultSource.uri;
- }
- nativeProps.progressHandlerRegistered = isNetwork && this.props.onLoadProgress;
- return ;
}
});
@@ -198,18 +176,7 @@ var styles = StyleSheet.create({
},
});
-var RCTNetworkImage = requireNativeComponent('RCTNetworkImageView', null);
-var RCTStaticImage = requireNativeComponent('RCTStaticImage', null);
-
-var nativeOnlyProps = {
- src: true,
- defaultImageSrc: true,
- imageTag: true,
- progressHandlerRegistered: true
-};
-if (__DEV__) {
- verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps);
- verifyPropTypes(Image, RCTNetworkImage.viewConfig, nativeOnlyProps);
-}
+var RCTImageView = requireNativeComponent('RCTImageView', null);
+var RCTNetworkImageView = (NativeModules.NetworkImageViewManager) ? requireNativeComponent('RCTNetworkImageView', null) : RCTImageView;
module.exports = Image;
diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
index 8ecabbafd..3eabd148e 100644
--- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
+++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
@@ -8,8 +8,8 @@
/* 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 */; };
- 1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; };
+ 1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */; };
+ 1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
1345A8391B26592900583190 /* RCTImageRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1345A8381B26592900583190 /* RCTImageRequestHandler.m */; };
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
@@ -17,8 +17,6 @@
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
- 58B511901A9E6BD600147676 /* RCTNetworkImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118C1A9E6BD600147676 /* RCTNetworkImageView.m */; };
- 58B511911A9E6BD600147676 /* RCTNetworkImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118E1A9E6BD600147676 /* RCTNetworkImageViewManager.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -36,10 +34,10 @@
/* Begin PBXFileReference section */
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTaskWrapper.h; sourceTree = ""; };
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTaskWrapper.m; sourceTree = ""; };
- 1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = ""; };
- 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = ""; };
- 1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = ""; };
- 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImageManager.m; sourceTree = ""; };
+ 1304D5A71AA8C4A30002E2BE /* RCTImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageView.h; sourceTree = ""; };
+ 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageView.m; sourceTree = ""; };
+ 1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageViewManager.h; sourceTree = ""; };
+ 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageViewManager.m; sourceTree = ""; };
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGIFImage.h; sourceTree = ""; };
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImage.m; sourceTree = ""; };
1345A8371B26592900583190 /* RCTImageRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageRequestHandler.h; sourceTree = ""; };
@@ -55,10 +53,6 @@
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 = ""; };
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = ""; };
- 58B5118B1A9E6BD600147676 /* RCTNetworkImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworkImageView.h; sourceTree = ""; };
- 58B5118C1A9E6BD600147676 /* RCTNetworkImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNetworkImageView.m; sourceTree = ""; };
- 58B5118D1A9E6BD600147676 /* RCTNetworkImageViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworkImageViewManager.h; sourceTree = ""; };
- 58B5118E1A9E6BD600147676 /* RCTNetworkImageViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNetworkImageViewManager.m; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -89,14 +83,10 @@
137620341B31C53500677FF0 /* RCTImagePickerManager.m */,
1345A8371B26592900583190 /* RCTImageRequestHandler.h */,
1345A8381B26592900583190 /* RCTImageRequestHandler.m */,
- 58B5118B1A9E6BD600147676 /* RCTNetworkImageView.h */,
- 58B5118C1A9E6BD600147676 /* RCTNetworkImageView.m */,
- 58B5118D1A9E6BD600147676 /* RCTNetworkImageViewManager.h */,
- 58B5118E1A9E6BD600147676 /* RCTNetworkImageViewManager.m */,
- 1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */,
- 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */,
- 1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */,
- 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */,
+ 1304D5A71AA8C4A30002E2BE /* RCTImageView.h */,
+ 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */,
+ 1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */,
+ 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */,
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
58B5115E1A9E6B3D00147676 /* Products */,
@@ -171,15 +161,13 @@
files = (
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */,
- 58B511911A9E6BD600147676 /* RCTNetworkImageViewManager.m in Sources */,
- 1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */,
+ 1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
1345A8391B26592900583190 /* RCTImageRequestHandler.m in Sources */,
- 58B511901A9E6BD600147676 /* RCTNetworkImageView.m in Sources */,
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */,
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */,
- 1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */,
+ 1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/Libraries/Image/RCTImageDownloader.h b/Libraries/Image/RCTImageDownloader.h
index 43bb9a69d..44ad1cde3 100644
--- a/Libraries/Image/RCTImageDownloader.h
+++ b/Libraries/Image/RCTImageDownloader.h
@@ -43,11 +43,4 @@ typedef void (^RCTImageDownloadCancellationBlock)(void);
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block;
-/**
- * Cancel an in-flight download. If multiple requets have been made for the
- * same image, only the request that relates to the token passed will be
- * cancelled.
- */
-- (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken;
-
@end
diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m
index f32d895cb..6cec0f478 100644
--- a/Libraries/Image/RCTImageDownloader.m
+++ b/Libraries/Image/RCTImageDownloader.m
@@ -52,7 +52,9 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
return self;
}
-- (RCTImageDownloadCancellationBlock)_downloadDataForURL:(NSURL *)url progressBlock:progressBlock block:(RCTCachedDataDownloadBlock)block
+- (RCTImageDownloadCancellationBlock)_downloadDataForURL:(NSURL *)url
+ progressBlock:progressBlock
+ block:(RCTCachedDataDownloadBlock)block
{
NSString *const cacheKey = url.absoluteString;
@@ -134,7 +136,9 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
return [cancel copy];
}
-- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock block:(RCTDataDownloadBlock)block
+- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url
+ progressBlock:(RCTDataProgressBlock)progressBlock
+ block:(RCTDataDownloadBlock)block
{
return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSURLResponse *response, NSData *data, NSError *error) {
block(data, error);
@@ -150,24 +154,19 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block
{
+ scale = scale ?: RCTScreenScale();
+
return [self downloadDataForURL:url progressBlock:progressBlock block:^(NSData *data, NSError *error) {
if (!data || error) {
block(nil, error);
return;
}
- if (CGSizeEqualToSize(size, CGSizeZero)) {
- // Target size wasn't available yet, so abort image drawing
- block(nil, nil);
- return;
- }
-
UIImage *image = [UIImage imageWithData:data scale:scale];
- if (image) {
+ if (image && !CGSizeEqualToSize(size, CGSizeZero)) {
// Get scale and size
- CGFloat destScale = scale ?: RCTScreenScale();
- CGRect imageRect = RCTClipRect(image.size, image.scale, size, destScale, resizeMode);
+ CGRect imageRect = RCTClipRect(image.size, scale, size, scale, resizeMode);
CGSize destSize = RCTTargetSizeForClipRect(imageRect);
// Opacity optimizations
@@ -183,7 +182,7 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
}
// Decompress image at required size
- UIGraphicsBeginImageContextWithOptions(destSize, opaque, destScale);
+ UIGraphicsBeginImageContextWithOptions(destSize, opaque, scale);
if (blendColor) {
[blendColor setFill];
UIRectFill((CGRect){CGPointZero, destSize});
@@ -201,11 +200,4 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
}];
}
-- (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken
-{
- if (downloadToken) {
- downloadToken();
- }
-}
-
@end
diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h
index 4337836fd..25fdb1b30 100644
--- a/Libraries/Image/RCTImageLoader.h
+++ b/Libraries/Image/RCTImageLoader.h
@@ -11,6 +11,10 @@
@class ALAssetsLibrary;
+typedef void (^RCTImageLoaderProgressBlock)(int64_t written, int64_t total);
+typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id /* UIImage or CAAnimation */);
+typedef void (^RCTImageLoaderCancellationBlock)(void);
+
@interface RCTImageLoader : NSObject
/**
@@ -22,22 +26,28 @@
* Can be called from any thread.
* Will always call callback on main thread.
*/
-+ (void)loadImageWithTag:(NSString *)imageTag
- callback:(void (^)(NSError *error, id /* UIImage or CAAnimation */ image))callback;
++ (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ callback:(RCTImageLoaderCompletionBlock)callback;
/**
* As above, but includes target size, scale and resizeMode, which are used to
* select the optimal dimensions for the loaded image.
*/
-+ (void)loadImageWithTag:(NSString *)imageTag
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
- callback:(void (^)(NSError *error, id /* UIImage or CAAnimation */ image))callback;
++ (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(UIViewContentMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progress
+ completionBlock:(RCTImageLoaderCompletionBlock)completion;
/**
* Is the specified image tag an asset library image?
*/
+ (BOOL)isAssetLibraryImage:(NSString *)imageTag;
+/**
+ * Is the specified image tag a remote image?
+ */
++ (BOOL)isRemoteImage:(NSString *)imageTag;
+
@end
diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m
index 69d98a60a..405b4907b 100644
--- a/Libraries/Image/RCTImageLoader.m
+++ b/Libraries/Image/RCTImageLoader.m
@@ -57,21 +57,23 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
return assetsLibrary;
}
-+ (void)loadImageWithTag:(NSString *)imageTag
- callback:(void (^)(NSError *error, id /* UIImage or CAAnimation */ image))callback
++ (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ callback:(RCTImageLoaderCompletionBlock)callback
{
return [self loadImageWithTag:imageTag
size:CGSizeZero
scale:0
resizeMode:UIViewContentModeScaleToFill
- callback:callback];
+ progressBlock:nil
+ completionBlock:callback];
}
-+ (void)loadImageWithTag:(NSString *)imageTag
- size:(CGSize)size
- scale:(CGFloat)scale
- resizeMode:(UIViewContentMode)resizeMode
- callback:(void (^)(NSError *error, id image))callback
++ (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
+ size:(CGSize)size
+ scale:(CGFloat)scale
+ resizeMode:(UIViewContentMode)resizeMode
+ progressBlock:(RCTImageLoaderProgressBlock)progress
+ completionBlock:(RCTImageLoaderCompletionBlock)completion
{
if ([imageTag hasPrefix:@"assets-library://"]) {
[[RCTImageLoader assetsLibrary] assetForURL:[NSURL URLWithString:imageTag] resultBlock:^(ALAsset *asset) {
@@ -109,19 +111,20 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
}
UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale orientation:(UIImageOrientation)orientation];
- RCTDispatchCallbackOnMainQueue(callback, nil, image);
+ RCTDispatchCallbackOnMainQueue(completion, nil, image);
}
});
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageTag];
NSError *error = RCTErrorWithMessage(errorText);
- RCTDispatchCallbackOnMainQueue(callback, error, nil);
+ RCTDispatchCallbackOnMainQueue(completion, error, nil);
}
} failureBlock:^(NSError *loadError) {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageTag, loadError];
NSError *error = RCTErrorWithMessage(errorText);
- RCTDispatchCallbackOnMainQueue(callback, error, nil);
+ RCTDispatchCallbackOnMainQueue(completion, error, nil);
}];
+ return ^{};
} else if ([imageTag hasPrefix:@"ph://"]) {
// Using PhotoKit for iOS 8+
// The 'ph://' prefix is used by FBMediaKit to differentiate between
@@ -132,8 +135,8 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
if (results.count == 0) {
NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID];
NSError *error = RCTErrorWithMessage(errorText);
- RCTDispatchCallbackOnMainQueue(callback, error, nil);
- return;
+ RCTDispatchCallbackOnMainQueue(completion, error, nil);
+ return ^{};
}
PHAsset *asset = [results firstObject];
@@ -144,59 +147,67 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
}
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:nil resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
- RCTDispatchCallbackOnMainQueue(callback, nil, result);
+ RCTDispatchCallbackOnMainQueue(completion, nil, result);
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load PHAsset with local identifier %@ with no error message.", phAssetID];
NSError *error = RCTErrorWithMessage(errorText);
- RCTDispatchCallbackOnMainQueue(callback, error, nil);
+ RCTDispatchCallbackOnMainQueue(completion, error, nil);
return;
}
}];
+ return ^{};
} else if ([imageTag hasPrefix:@"http"]) {
NSURL *url = [NSURL URLWithString:imageTag];
if (!url) {
NSString *errorMessage = [NSString stringWithFormat:@"Invalid URL: %@", imageTag];
- RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil);
- return;
+ RCTDispatchCallbackOnMainQueue(completion, RCTErrorWithMessage(errorMessage), nil);
+ return ^{};
}
- if ([[imageTag lowercaseString] hasSuffix:@".gif"]) {
- [[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:nil block:^(NSData *data, NSError *error) {
+ if ([imageTag.lowercaseString hasSuffix:@".gif"]) {
+ return [[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:progress block:^(NSData *data, NSError *error) {
id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
if (!image && !error) {
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag];
error = RCTErrorWithMessage(errorMessage);
}
- RCTDispatchCallbackOnMainQueue(callback, error, image);
+ RCTDispatchCallbackOnMainQueue(completion, error, image);
}];
} else {
- [[RCTImageDownloader sharedInstance] downloadImageForURL:url size:size scale:scale resizeMode:resizeMode tintColor:nil backgroundColor:nil progressBlock:NULL block:^(UIImage *image, NSError *error) {
- RCTDispatchCallbackOnMainQueue(callback, error, image);
+ return [[RCTImageDownloader sharedInstance] downloadImageForURL:url size:size scale:scale resizeMode:resizeMode tintColor:nil backgroundColor:nil progressBlock:progress block:^(UIImage *image, NSError *error) {
+ RCTDispatchCallbackOnMainQueue(completion, error, image);
}];
}
- } else if ([[imageTag lowercaseString] hasSuffix:@".gif"]) {
+ } else if ([imageTag.lowercaseString hasSuffix:@".gif"]) {
id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
if (image) {
- RCTDispatchCallbackOnMainQueue(callback, nil, image);
+ RCTDispatchCallbackOnMainQueue(completion, nil, image);
} else {
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag];
NSError *error = RCTErrorWithMessage(errorMessage);
- RCTDispatchCallbackOnMainQueue(callback, error, nil);
+ RCTDispatchCallbackOnMainQueue(completion, error, nil);
}
+ return ^{};
} else {
UIImage *image = [RCTConvert UIImage:imageTag];
if (image) {
- RCTDispatchCallbackOnMainQueue(callback, nil, image);
+ RCTDispatchCallbackOnMainQueue(completion, nil, image);
} else {
NSString *errorMessage = [NSString stringWithFormat:@"Unrecognized tag protocol: %@", imageTag];
NSError *error = RCTErrorWithMessage(errorMessage);
- RCTDispatchCallbackOnMainQueue(callback, error, nil);
+ RCTDispatchCallbackOnMainQueue(completion, error, nil);
}
+ return ^{};
}
}
+ (BOOL)isAssetLibraryImage:(NSString *)imageTag
{
- return [imageTag hasPrefix:@"assets-library://"] || [imageTag hasPrefix:@"ph:"];
+ return [imageTag hasPrefix:@"assets-library://"] || [imageTag hasPrefix:@"ph://"];
+}
+
++ (BOOL)isRemoteImage:(NSString *)imageTag
+{
+ return [imageTag hasPrefix:@"http://"] || [imageTag hasPrefix:@"https://"];
}
@end
diff --git a/Libraries/Image/RCTStaticImage.h b/Libraries/Image/RCTImageView.h
similarity index 72%
rename from Libraries/Image/RCTStaticImage.h
rename to Libraries/Image/RCTImageView.h
index c8f46a302..fff7c96a0 100644
--- a/Libraries/Image/RCTStaticImage.h
+++ b/Libraries/Image/RCTImageView.h
@@ -9,9 +9,14 @@
#import
-@interface RCTStaticImage : UIImageView
+@class RCTBridge;
+
+@interface RCTImageView : UIImageView
+
+- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
@property (nonatomic, assign) UIEdgeInsets capInsets;
+@property (nonatomic, strong) UIImage *defaultImage;
@property (nonatomic, assign) UIImageRenderingMode renderingMode;
@property (nonatomic, copy) NSString *src;
diff --git a/Libraries/Image/RCTStaticImage.m b/Libraries/Image/RCTImageView.m
similarity index 59%
rename from Libraries/Image/RCTStaticImage.m
rename to Libraries/Image/RCTImageView.m
index 0e9d4b608..8773aebb7 100644
--- a/Libraries/Image/RCTStaticImage.m
+++ b/Libraries/Image/RCTImageView.m
@@ -7,16 +7,41 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
-#import "RCTStaticImage.h"
+#import "RCTImageView.h"
+#import "RCTBridge.h"
#import "RCTConvert.h"
+#import "RCTEventDispatcher.h"
#import "RCTGIFImage.h"
#import "RCTImageLoader.h"
#import "RCTUtils.h"
#import "UIView+React.h"
-@implementation RCTStaticImage
+@interface RCTImageView ()
+
+@property (nonatomic, assign) BOOL onLoadStart;
+@property (nonatomic, assign) BOOL onProgress;
+@property (nonatomic, assign) BOOL onError;
+@property (nonatomic, assign) BOOL onLoad;
+@property (nonatomic, assign) BOOL onLoadEnd;
+
+@end
+
+@implementation RCTImageView
+{
+ RCTBridge *_bridge;
+}
+
+- (instancetype)initWithBridge:(RCTBridge *)bridge
+{
+ if ((self = [super init])) {
+ _bridge = bridge;
+ }
+ return self;
+}
+
+RCT_NOT_IMPLEMENTED(-init)
- (void)_updateImage
{
@@ -45,7 +70,7 @@
- (void)setImage:(UIImage *)image
{
if (image != super.image) {
- super.image = image;
+ super.image = image ?: _defaultImage;
[self _updateImage];
}
}
@@ -77,19 +102,55 @@
- (void)reloadImage
{
if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) {
+
+ if (_onLoadStart) {
+ NSDictionary *event = @{ @"target": self.reactTag };
+ [_bridge.eventDispatcher sendInputEventWithName:@"loadStart" body:event];
+ }
+
+ RCTImageLoaderProgressBlock progressHandler = nil;
+ if (_onProgress) {
+ progressHandler = ^(int64_t loaded, int64_t total) {
+ NSDictionary *event = @{
+ @"target": self.reactTag,
+ @"loaded": @(loaded),
+ @"total": @(total),
+ };
+ [_bridge.eventDispatcher sendInputEventWithName:@"progress" body:event];
+ };
+ }
+
[RCTImageLoader loadImageWithTag:_src
size:self.bounds.size
scale:RCTScreenScale()
- resizeMode:self.contentMode callback:^(NSError *error, id image) {
- if (error) {
- RCTLogWarn(@"%@", error.localizedDescription);
- }
+ resizeMode:self.contentMode
+ progressBlock:progressHandler
+ completionBlock:^(NSError *error, id image) {
+
if ([image isKindOfClass:[CAAnimation class]]) {
[self.layer addAnimation:image forKey:@"contents"];
} else {
[self.layer removeAnimationForKey:@"contents"];
self.image = image;
}
+ if (error) {
+ if (_onError) {
+ NSDictionary *event = @{
+ @"target": self.reactTag,
+ @"error": error.localizedDescription,
+ };
+ [_bridge.eventDispatcher sendInputEventWithName:@"error" body:event];
+ }
+ } else {
+ if (_onLoad) {
+ NSDictionary *event = @{ @"target": self.reactTag };
+ [_bridge.eventDispatcher sendInputEventWithName:@"load" body:event];
+ }
+ }
+ if (_onLoadEnd) {
+ NSDictionary *event = @{ @"target": self.reactTag };
+ [_bridge.eventDispatcher sendInputEventWithName:@"loadEnd" body:event];
+ }
}];
} else {
[self.layer removeAnimationForKey:@"contents"];
@@ -102,7 +163,7 @@
[super reactSetFrame:frame];
if (self.image == nil) {
[self reloadImage];
- } else if ([RCTImageLoader isAssetLibraryImage:_src]) {
+ } else if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) {
CGSize imageSize = {
self.image.size.width / RCTScreenScale(),
self.image.size.height / RCTScreenScale()
diff --git a/Libraries/Image/RCTStaticImageManager.h b/Libraries/Image/RCTImageViewManager.h
similarity index 87%
rename from Libraries/Image/RCTStaticImageManager.h
rename to Libraries/Image/RCTImageViewManager.h
index b02f9fe11..4e8d3fac4 100644
--- a/Libraries/Image/RCTStaticImageManager.h
+++ b/Libraries/Image/RCTImageViewManager.h
@@ -9,6 +9,6 @@
#import "RCTViewManager.h"
-@interface RCTStaticImageManager : RCTViewManager
+@interface RCTImageViewManager : RCTViewManager
@end
diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.m
new file mode 100644
index 000000000..28f93466a
--- /dev/null
+++ b/Libraries/Image/RCTImageViewManager.m
@@ -0,0 +1,57 @@
+/**
+ * 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 "RCTImageViewManager.h"
+
+#import
+
+#import "RCTConvert.h"
+#import "RCTImageView.h"
+
+@implementation RCTImageViewManager
+
+RCT_EXPORT_MODULE()
+
+- (UIView *)view
+{
+ return [[RCTImageView alloc] initWithBridge:self.bridge];
+}
+
+RCT_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets)
+RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage)
+RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
+RCT_EXPORT_VIEW_PROPERTY(src, NSString)
+RCT_EXPORT_VIEW_PROPERTY(onLoadStart, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(onProgress, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(onError, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(onLoad, BOOL)
+RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, BOOL)
+RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView)
+{
+ if (json) {
+ view.renderingMode = UIImageRenderingModeAlwaysTemplate;
+ view.tintColor = [RCTConvert UIColor:json];
+ } else {
+ view.renderingMode = defaultView.renderingMode;
+ view.tintColor = defaultView.tintColor;
+ }
+}
+
+- (NSDictionary *)customDirectEventTypes
+{
+ return @{
+ @"loadStart": @{ @"registrationName": @"onLoadStart" },
+ @"progress": @{ @"registrationName": @"onProgress" },
+ @"error": @{ @"registrationName": @"onError" },
+ @"load": @{ @"registrationName": @"onLoad" },
+ @"loadEnd": @{ @"registrationName": @"onLoadEnd" },
+ };
+}
+
+@end
diff --git a/Libraries/Image/RCTNetworkImageView.h b/Libraries/Image/RCTNetworkImageView.h
deleted file mode 100644
index 6dd73e9aa..000000000
--- a/Libraries/Image/RCTNetworkImageView.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * 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
-
-@class RCTEventDispatcher;
-@class RCTImageDownloader;
-
-@interface RCTNetworkImageView : UIView
-
-- (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,
- * or when imageURL is nil. Defaults to nil.
- */
-@property (nonatomic, strong) UIImage *defaultImage;
-
-/**
- * Specify a URL for an image. The image will be asynchronously loaded and displayed.
- */
-@property (nonatomic, strong) NSURL *imageURL;
-
-/**
- * Whether the image should be masked with this view's tint color.
- */
-@property (nonatomic, assign) BOOL tinted;
-
-/**
- * By default, changing imageURL will reset whatever existing image was present
- * and revert to defaultImage while the new image loads. In certain obscure cases you
- * may want to disable this behavior and instead keep displaying the previous image
- * while the new one loads. In this case, pass NO for resetToDefaultImageWhileLoading.
- * (If you set imageURL to nil, however, resetToDefaultImageWhileLoading is ignored;
- * that will always reset to the default image.)
- */
-- (void)setImageURL:(NSURL *)imageURL resetToDefaultImageWhileLoading:(BOOL)reset;
-
-@end
diff --git a/Libraries/Image/RCTNetworkImageView.m b/Libraries/Image/RCTNetworkImageView.m
deleted file mode 100644
index 20d297b46..000000000
--- a/Libraries/Image/RCTNetworkImageView.m
+++ /dev/null
@@ -1,220 +0,0 @@
-/**
- * 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 "RCTNetworkImageView.h"
-
-#import "RCTAssert.h"
-#import "RCTConvert.h"
-#import "RCTGIFImage.h"
-#import "RCTImageDownloader.h"
-#import "RCTUtils.h"
-#import "RCTBridgeModule.h"
-#import "RCTEventDispatcher.h"
-#import "UIView+React.h"
-
-@implementation RCTNetworkImageView
-{
- BOOL _deferred;
- BOOL _progressHandlerRegistered;
- NSURL *_imageURL;
- NSURL *_deferredImageURL;
- NSUInteger _deferSentinel;
- RCTImageDownloader *_imageDownloader;
- id _downloadToken;
- RCTEventDispatcher *_eventDispatcher;
-}
-
-- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher imageDownloader:(RCTImageDownloader *)imageDownloader
-{
- if ((self = [super initWithFrame:CGRectZero])) {
- _eventDispatcher = eventDispatcher;
- _progressHandlerRegistered = NO;
- _deferSentinel = 0;
- _imageDownloader = imageDownloader;
- self.userInteractionEnabled = NO;
- self.contentMode = UIViewContentModeScaleAspectFill;
- }
- return self;
-}
-
-RCT_NOT_IMPLEMENTED(-initWithFrame:(CGRect)frame)
-RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
-
-- (NSURL *)imageURL
-{
- // We clear our imageURL when we are not in a window for a while,
- // to make sure we don't consume network resources while offscreen.
- // However we don't want to expose this hackery externally.
- return _deferred ? _deferredImageURL : _imageURL;
-}
-
-- (void)setBackgroundColor:(UIColor *)backgroundColor
-{
- super.backgroundColor = backgroundColor;
- [self _updateImage];
-}
-
-- (void)setTintColor:(UIColor *)tintColor
-{
- super.tintColor = tintColor;
- [self _updateImage];
-}
-
-- (void)setProgressHandlerRegistered:(BOOL)progressHandlerRegistered
-{
- _progressHandlerRegistered = progressHandlerRegistered;
-}
-
-- (void)reactSetFrame:(CGRect)frame
-{
- [super reactSetFrame:frame];
- [self _updateImage];
-}
-
-- (void)_updateImage
-{
- [self setImageURL:_imageURL resetToDefaultImageWhileLoading:NO];
-}
-
-- (void)setImageURL:(NSURL *)imageURL resetToDefaultImageWhileLoading:(BOOL)reset
-{
- if (![_imageURL isEqual:imageURL] && _downloadToken) {
- [_imageDownloader cancelDownload:_downloadToken];
- NSDictionary *event = @{ @"target": self.reactTag };
- [_eventDispatcher sendInputEventWithName:@"loadAbort" body:event];
- _downloadToken = nil;
- }
-
- _imageURL = imageURL;
-
- if (_deferred) {
- _deferredImageURL = imageURL;
- } else {
- if (!imageURL) {
- self.layer.contents = nil;
- return;
- }
- if (reset) {
- self.layer.contentsScale = _defaultImage.scale;
- self.layer.contents = (__bridge id)_defaultImage.CGImage;
- self.layer.minificationFilter = 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) {
- _downloadToken = [_imageDownloader downloadDataForURL:imageURL progressBlock:progressHandler block:^(NSData *data, NSError *error) {
- if (data) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (imageURL != self.imageURL) {
- // Image has changed
- return;
- }
- CAKeyframeAnimation *animation = RCTGIFImageWithData(data);
- self.layer.contentsScale = 1.0;
- self.layer.minificationFilter = kCAFilterLinear;
- self.layer.magnificationFilter = kCAFilterLinear;
- [self.layer addAnimation:animation forKey:@"contents"];
- loadEndHandler();
- });
- } else if (error) {
- errorHandler([error localizedDescription]);
- }
- }];
- } else {
- _downloadToken = [_imageDownloader downloadImageForURL:imageURL
- size:self.bounds.size
- scale:RCTScreenScale()
- resizeMode:self.contentMode
- tintColor:_tinted ? self.tintColor : nil
- backgroundColor:self.backgroundColor
- progressBlock:progressHandler
- block:^(UIImage *image, NSError *error) {
- if (image) {
- dispatch_async(dispatch_get_main_queue(), ^{
- if (imageURL != self.imageURL) {
- // Image has changed
- return;
- }
- [self.layer removeAnimationForKey:@"contents"];
- self.layer.contentsScale = image.scale;
- self.layer.contents = (__bridge id)image.CGImage;
- loadEndHandler();
- });
- } else if (error) {
- errorHandler([error localizedDescription]);
- }
- }];
- }
- }
-}
-
-- (void)setImageURL:(NSURL *)imageURL
-{
- [self setImageURL:imageURL resetToDefaultImageWhileLoading:YES];
-}
-
-- (void)willMoveToWindow:(UIWindow *)newWindow
-{
- [super willMoveToWindow:newWindow];
- if (newWindow != nil && _deferredImageURL) {
- // Immediately exit deferred mode and restore the imageURL that we saved when we went offscreen.
- [self setImageURL:_deferredImageURL resetToDefaultImageWhileLoading:YES];
- _deferredImageURL = nil;
- }
-}
-
-- (void)_enterDeferredModeIfNeededForSentinel:(NSUInteger)sentinel
-{
- if (self.window == nil && _deferSentinel == sentinel) {
- _deferred = YES;
- [_imageDownloader cancelDownload:_downloadToken];
- _downloadToken = nil;
- _deferredImageURL = _imageURL;
- _imageURL = nil;
- }
-}
-
-- (void)didMoveToWindow
-{
- [super didMoveToWindow];
- if (self.window == nil) {
- __weak RCTNetworkImageView *weakSelf = self;
- NSUInteger sentinelAtDispatchTime = ++_deferSentinel;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC), dispatch_get_main_queue(), ^(void){
- [weakSelf _enterDeferredModeIfNeededForSentinel:sentinelAtDispatchTime];
- });
- }
-}
-
-@end
diff --git a/Libraries/Image/RCTNetworkImageViewManager.h b/Libraries/Image/RCTNetworkImageViewManager.h
deleted file mode 100644
index 3176ce896..000000000
--- a/Libraries/Image/RCTNetworkImageViewManager.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * 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 "RCTViewManager.h"
-
-@interface RCTNetworkImageViewManager : RCTViewManager
-
-@end
-
diff --git a/Libraries/Image/RCTNetworkImageViewManager.m b/Libraries/Image/RCTNetworkImageViewManager.m
deleted file mode 100644
index f42ef48f1..000000000
--- a/Libraries/Image/RCTNetworkImageViewManager.m
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * 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 "RCTNetworkImageViewManager.h"
-
-#import "RCTBridge.h"
-#import "RCTConvert.h"
-#import "RCTImageDownloader.h"
-#import "RCTNetworkImageView.h"
-#import "RCTUtils.h"
-
-@implementation RCTNetworkImageViewManager
-
-RCT_EXPORT_MODULE()
-
-@synthesize bridge = _bridge;
-@synthesize methodQueue = _methodQueue;
-
-- (UIView *)view
-{
- return [[RCTNetworkImageView alloc] initWithEventDispatcher:self.bridge.eventDispatcher imageDownloader:[RCTImageDownloader sharedInstance]];
-}
-
-RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage)
-RCT_REMAP_VIEW_PROPERTY(src, imageURL, NSURL)
-RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
-RCT_EXPORT_VIEW_PROPERTY(progressHandlerRegistered, BOOL)
-RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTNetworkImageView)
-{
- if (json) {
- view.tinted = YES;
- view.tintColor = [RCTConvert UIColor:json];
- } else {
- view.tinted = defaultView.tinted;
- view.tintColor = defaultView.tintColor;
- }
-}
-
-- (NSDictionary *)customDirectEventTypes
-{
- return @{
- @"loadStart": @{ @"registrationName": @"onLoadStart" },
- @"loadProgress": @{ @"registrationName": @"onLoadProgress" },
- @"loaded": @{ @"registrationName": @"onLoadEnd" },
- @"loadError": @{ @"registrationName": @"onLoadError" },
- @"loadAbort": @{ @"registrationName": @"onLoadAbort" },
- };
-}
-
-@end
diff --git a/Libraries/Image/RCTStaticImageManager.m b/Libraries/Image/RCTStaticImageManager.m
deleted file mode 100644
index 7b3fb16db..000000000
--- a/Libraries/Image/RCTStaticImageManager.m
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * 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 "RCTStaticImageManager.h"
-
-#import
-
-#import "RCTConvert.h"
-#import "RCTStaticImage.h"
-
-@implementation RCTStaticImageManager
-
-RCT_EXPORT_MODULE()
-
-- (UIView *)view
-{
- return [[RCTStaticImage alloc] init];
-}
-
-RCT_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets)
-RCT_REMAP_VIEW_PROPERTY(imageTag, src, NSString)
-RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
-RCT_EXPORT_VIEW_PROPERTY(src, NSString)
-RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTStaticImage)
-{
- if (json) {
- view.renderingMode = UIImageRenderingModeAlwaysTemplate;
- view.tintColor = [RCTConvert UIColor:json];
- } else {
- view.renderingMode = defaultView.renderingMode;
- view.tintColor = defaultView.tintColor;
- }
-}
-
-@end
diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m
index 724ace6e6..d1af57705 100644
--- a/React/Modules/RCTUIManager.m
+++ b/React/Modules/RCTUIManager.m
@@ -1408,8 +1408,13 @@ RCT_EXPORT_METHOD(clearJSResponder)
for (RCTViewManager *manager in _viewManagers.allValues) {
if (RCTClassOverridesInstanceMethod([manager class], @selector(customDirectEventTypes))) {
NSDictionary *eventTypes = [manager customDirectEventTypes];
- for (NSString *eventName in eventTypes) {
- RCTAssert(!customDirectEventTypes[eventName], @"Event '%@' registered multiple times.", eventName);
+ if (RCT_DEV) {
+ for (NSString *eventName in eventTypes) {
+ id eventType = customDirectEventTypes[eventName];
+ RCTAssert(!eventType || [eventType isEqual:eventTypes[eventName]],
+ @"Event '%@' registered multiple times with different "
+ "properties.", eventName);
+ }
}
[customDirectEventTypes addEntriesFromDictionary:eventTypes];
}