diff --git a/Examples/UIExplorer/TextExample.ios.js b/Examples/UIExplorer/TextExample.ios.js
index cceca74e6..0d6e20c10 100644
--- a/Examples/UIExplorer/TextExample.ios.js
+++ b/Examples/UIExplorer/TextExample.ios.js
@@ -17,6 +17,7 @@
var React = require('react-native');
var {
+ Image,
StyleSheet,
Text,
View,
@@ -397,6 +398,17 @@ exports.examples = [
);
},
+}, {
+ title: 'Inline images',
+ render: function() {
+ return (
+
+
+ This text contains an inline image . Neat, huh?
+
+
+ );
+ },
}];
var styles = StyleSheet.create({
diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js
index 59084721e..4da2d53e5 100644
--- a/Libraries/Image/Image.ios.js
+++ b/Libraries/Image/Image.ios.js
@@ -153,6 +153,10 @@ var Image = React.createClass({
validAttributes: ReactNativeViewAttributes.UIView
},
+ contextTypes: {
+ isInAParentText: React.PropTypes.bool
+ },
+
render: function() {
for (var prop in cfg.nativeOnly) {
if (this.props[prop] !== undefined) {
@@ -182,16 +186,20 @@ var Image = React.createClass({
RawImage = RCTImageView;
}
- return (
-
- );
+ if (this.context.isInAParentText) {
+ return ;
+ } else {
+ return (
+
+ );
+ }
}
});
@@ -210,6 +218,7 @@ var cfg = {
},
};
var RCTImageView = requireNativeComponent('RCTImageView', Image, cfg);
-var RCTNetworkImageView = (NativeModules.NetworkImageViewManager) ? requireNativeComponent('RCTNetworkImageView', Image, cfg) : RCTImageView;
+var RCTNetworkImageView = NativeModules.NetworkImageViewManager ? requireNativeComponent('RCTNetworkImageView', Image, cfg) : RCTImageView;
+var RCTVirtualImage = requireNativeComponent('RCTVirtualImage', Image);
module.exports = Image;
diff --git a/Libraries/Image/RCTGIFImageDecoder.m b/Libraries/Image/RCTGIFImageDecoder.m
index 332769e7d..899a266e8 100644
--- a/Libraries/Image/RCTGIFImageDecoder.m
+++ b/Libraries/Image/RCTGIFImageDecoder.m
@@ -48,7 +48,7 @@ RCT_EXPORT_MODULE()
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
if (!image) {
- image = [UIImage imageWithCGImage:imageRef];
+ image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
}
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
@@ -98,7 +98,7 @@ RCT_EXPORT_MODULE()
// Don't bother creating an animation
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
if (imageRef) {
- image = [UIImage imageWithCGImage:imageRef];
+ image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
CFRelease(imageRef);
}
CFRelease(imageSource);
diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
index 183cb850e..a6412bcc3 100644
--- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
+++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj
@@ -11,6 +11,8 @@
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; };
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
+ 13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */; settings = {ASSET_TAGS = (); }; };
+ 13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */; settings = {ASSET_TAGS = (); }; };
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
@@ -39,6 +41,10 @@
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = ""; };
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = ""; };
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = ""; };
+ 13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowVirtualImage.h; sourceTree = ""; };
+ 13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowVirtualImage.m; sourceTree = ""; };
+ 13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualImageManager.h; sourceTree = ""; };
+ 13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualImageManager.m; sourceTree = ""; };
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = ""; };
143879371AAD32A300F088A5 /* RCTImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoader.m; sourceTree = ""; };
35123E691B59C99D00EBAD80 /* RCTImageStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageStoreManager.h; sourceTree = ""; };
@@ -84,6 +90,10 @@
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */,
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
+ 13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */,
+ 13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */,
+ 13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */,
+ 13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */,
58B5115E1A9E6B3D00147676 /* Products */,
);
indentWidth = 2;
@@ -154,6 +164,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */,
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
@@ -161,6 +172,7 @@
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */,
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
+ 13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */,
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */,
83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */,
);
diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m
index e299b1328..5b8befb8b 100644
--- a/Libraries/Image/RCTImageDownloader.m
+++ b/Libraries/Image/RCTImageDownloader.m
@@ -135,7 +135,7 @@ RCT_EXPORT_MODULE()
// Normally -dataWithContentsOfURL: would be bad but this is a data URL.
NSData *data = [NSData dataWithContentsOfURL:imageURL];
- UIImage *image = [UIImage imageWithData:data];
+ UIImage *image = [UIImage imageWithData:data scale:scale];
if (image) {
if (progressHandler) {
progressHandler(1, 1);
diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m
index 7a3c235a2..d91096243 100644
--- a/Libraries/Image/RCTImageLoader.m
+++ b/Libraries/Image/RCTImageLoader.m
@@ -14,6 +14,7 @@
#import "RCTConvert.h"
#import "RCTDefines.h"
#import "RCTImageDownloader.h"
+#import "RCTImageUtils.h"
#import "RCTLog.h"
#import "RCTUtils.h"
@@ -165,7 +166,7 @@ RCT_EXPORT_MODULE()
}];
} else {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- UIImage *image = [UIImage imageWithData:data];
+ UIImage *image = [UIImage imageWithData:data scale:scale];
if (image) {
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
} else {
diff --git a/Libraries/Image/RCTImageView.h b/Libraries/Image/RCTImageView.h
index fff7c96a0..a561c0f7f 100644
--- a/Libraries/Image/RCTImageView.h
+++ b/Libraries/Image/RCTImageView.h
@@ -8,10 +8,11 @@
*/
#import
+#import "RCTImageComponent.h"
@class RCTBridge;
-@interface RCTImageView : UIImageView
+@interface RCTImageView : UIImageView
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m
index 104308131..5926f6331 100644
--- a/Libraries/Image/RCTImageView.m
+++ b/Libraries/Image/RCTImageView.m
@@ -22,7 +22,8 @@
* Determines whether an image of `currentSize` should be reloaded for display
* at `idealSize`.
*/
-static BOOL RCTShouldReloadImageForSizeChange(CGSize currentSize, CGSize idealSize) {
+static BOOL RCTShouldReloadImageForSizeChange(CGSize currentSize, CGSize idealSize)
+{
static const CGFloat upscaleThreshold = 1.2;
static const CGFloat downscaleThreshold = 0.5;
diff --git a/Libraries/Image/RCTShadowVirtualImage.h b/Libraries/Image/RCTShadowVirtualImage.h
new file mode 100644
index 000000000..841c215e2
--- /dev/null
+++ b/Libraries/Image/RCTShadowVirtualImage.h
@@ -0,0 +1,25 @@
+/**
+ * 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 "RCTShadowView.h"
+#import "RCTImageComponent.h"
+
+@class RCTBridge;
+
+/**
+ * Shadow image component, used for embedding images in non-view contexts such
+ * as text. This is NOT used for ordinary views.
+ */
+@interface RCTShadowVirtualImage : RCTShadowView
+
+- (instancetype)initWithBridge:(RCTBridge *)bridge;
+
+@property (nonatomic, copy) NSDictionary *source;
+
+@end
diff --git a/Libraries/Image/RCTShadowVirtualImage.m b/Libraries/Image/RCTShadowVirtualImage.m
new file mode 100644
index 000000000..57dbef04b
--- /dev/null
+++ b/Libraries/Image/RCTShadowVirtualImage.m
@@ -0,0 +1,48 @@
+/**
+ * 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 "RCTShadowVirtualImage.h"
+#import "RCTImageLoader.h"
+#import "RCTBridge.h"
+#import "RCTConvert.h"
+
+@implementation RCTShadowVirtualImage
+{
+ RCTBridge *_bridge;
+}
+
+@synthesize image = _image;
+
+- (instancetype)initWithBridge:(RCTBridge *)bridge
+{
+ if ((self = [super init])) {
+ _bridge = bridge;
+ }
+ return self;
+}
+
+RCT_NOT_IMPLEMENTED(-(instancetype)init)
+
+- (void)setSource:(NSDictionary *)source
+{
+ if (![source isEqual:_source]) {
+ _source = [source copy];
+ NSString *imageTag = [RCTConvert NSString:_source[@"uri"]];
+ CGFloat scale = [RCTConvert CGFloat:_source[@"scale"]] ?: 1;
+
+ __weak RCTShadowVirtualImage *weakSelf = self;
+ [_bridge.imageLoader loadImageWithTag:imageTag size:CGSizeZero scale:scale resizeMode:UIViewContentModeScaleToFill progressBlock:nil completionBlock:^(NSError *error, UIImage *image) {
+ RCTShadowVirtualImage *strongSelf = weakSelf;
+ strongSelf->_image = image;
+ [strongSelf dirtyText];
+ }];
+ }
+}
+
+@end
diff --git a/Libraries/Image/RCTVirtualImageManager.h b/Libraries/Image/RCTVirtualImageManager.h
new file mode 100644
index 000000000..b92896235
--- /dev/null
+++ b/Libraries/Image/RCTVirtualImageManager.h
@@ -0,0 +1,14 @@
+/**
+ * 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 RCTVirtualImageManager : RCTViewManager
+
+@end
diff --git a/Libraries/Image/RCTVirtualImageManager.m b/Libraries/Image/RCTVirtualImageManager.m
new file mode 100644
index 000000000..2800a92b0
--- /dev/null
+++ b/Libraries/Image/RCTVirtualImageManager.m
@@ -0,0 +1,24 @@
+/**
+ * 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 "RCTVirtualImageManager.h"
+#import "RCTShadowVirtualImage.h"
+
+@implementation RCTVirtualImageManager
+
+RCT_EXPORT_MODULE()
+
+- (RCTShadowView *)shadowView
+{
+ return [[RCTShadowVirtualImage alloc] initWithBridge:self.bridge];
+}
+
+RCT_EXPORT_SHADOW_PROPERTY(source, NSDictionary)
+
+@end
diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m
index b9c1ff2e7..357c3784c 100644
--- a/Libraries/Text/RCTShadowText.m
+++ b/Libraries/Text/RCTShadowText.m
@@ -13,6 +13,7 @@
#import "RCTUIManager.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
+#import "RCTImageComponent.h"
#import "RCTLog.h"
#import "RCTShadowRawText.h"
#import "RCTSparseArray.h"
@@ -191,8 +192,17 @@ static css_dim_t RCTMeasure(void *context, float width)
} else if ([child isKindOfClass:[RCTShadowRawText class]]) {
RCTShadowRawText *shadowRawText = (RCTShadowRawText *)child;
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:shadowRawText.text ?: @""]];
+ } else if ([child conformsToProtocol:@protocol(RCTImageComponent)]) {
+ UIImage *image = ((id)child).image;
+ if (image) {
+ NSTextAttachment *imageAttachment = [NSTextAttachment new];
+ imageAttachment.image = image;
+ [attributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:imageAttachment]];
+ } else {
+ //TODO: add placeholder image?
+ }
} else {
- RCTLogError(@" can't have any children except or raw strings");
+ RCTLogError(@" can't have any children except , or raw strings");
}
[child setTextComputed];
diff --git a/React/Modules/RCTAccessibilityManager.m b/React/Modules/RCTAccessibilityManager.m
index 4ee7b811c..f7bfe8feb 100644
--- a/React/Modules/RCTAccessibilityManager.m
+++ b/React/Modules/RCTAccessibilityManager.m
@@ -26,7 +26,6 @@ NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification = @"RCTAc
@synthesize bridge = _bridge;
@synthesize multipliers = _multipliers;
-@synthesize isVoiceOverEnabled = _isVoiceOverEnabled;
RCT_EXPORT_MODULE()
diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj
index 49959ea17..be9efcb83 100644
--- a/React/React.xcodeproj/project.pbxproj
+++ b/React/React.xcodeproj/project.pbxproj
@@ -189,6 +189,7 @@
13E067501A70F44B002CDEE1 /* RCTView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTView.m; sourceTree = ""; };
13E067531A70F44B002CDEE1 /* UIView+React.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+React.h"; sourceTree = ""; };
13E067541A70F44B002CDEE1 /* UIView+React.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+React.m"; sourceTree = ""; };
+ 13EF7F441BC69646003F47DD /* RCTImageComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTImageComponent.h; sourceTree = ""; };
13F17A831B8493E5007D4C75 /* RCTRedBox.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRedBox.h; sourceTree = ""; };
13F17A841B8493E5007D4C75 /* RCTRedBox.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRedBox.m; sourceTree = ""; };
1403F2B11B0AE60700C2A9A4 /* RCTPerfStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPerfStats.h; sourceTree = ""; };
@@ -341,6 +342,7 @@
13C325281AA63B6A0048765F /* RCTComponent.h */,
13AB90BF1B6FA36700713B4F /* RCTComponentData.h */,
13AB90C01B6FA36700713B4F /* RCTComponentData.m */,
+ 13EF7F441BC69646003F47DD /* RCTImageComponent.h */,
13456E911ADAD2DE009F94A7 /* RCTConvert+CoreLocation.h */,
13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */,
13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */,
diff --git a/React/Views/RCTImageComponent.h b/React/Views/RCTImageComponent.h
new file mode 100644
index 000000000..a6916c9aa
--- /dev/null
+++ b/React/Views/RCTImageComponent.h
@@ -0,0 +1,19 @@
+/**
+ * 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
+
+/**
+ * Generic interface for components that contain an image.
+ */
+@protocol RCTImageComponent
+
+@property (nonatomic, strong, readonly) UIImage *image;
+
+@end