Skip to content

Commit 91e6c98

Browse files
nicklockwoodfacebook-github-bot-0
authored andcommitted
Implemented inline image support for <Text>
Summary: @​public This diff implements inline image support for <Text> nodes. Images are specified using <Image> tags, however all properties of the image are currently ignored apart from the source (including width/height styles). Images are loaded asyncronously, and will trigger a text re-layout when they have loaded. Reviewed By: @javache Differential Revision: D2507725 fb-gh-sync-id: 59d0696d00a1bc531915cc35242a16b2dec96e85
1 parent c740eda commit 91e6c98

16 files changed

Lines changed: 196 additions & 19 deletions

Examples/UIExplorer/TextExample.ios.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
var React = require('react-native');
1919
var {
20+
Image,
2021
StyleSheet,
2122
Text,
2223
View,
@@ -397,6 +398,17 @@ exports.examples = [
397398
</View>
398399
);
399400
},
401+
}, {
402+
title: 'Inline images',
403+
render: function() {
404+
return (
405+
<View>
406+
<Text>
407+
This text contains an inline image <Image source={require('./flux.png')}/>. Neat, huh?
408+
</Text>
409+
</View>
410+
);
411+
},
400412
}];
401413

402414
var styles = StyleSheet.create({

Libraries/Image/Image.ios.js

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ var Image = React.createClass({
153153
validAttributes: ReactNativeViewAttributes.UIView
154154
},
155155

156+
contextTypes: {
157+
isInAParentText: React.PropTypes.bool
158+
},
159+
156160
render: function() {
157161
for (var prop in cfg.nativeOnly) {
158162
if (this.props[prop] !== undefined) {
@@ -182,16 +186,20 @@ var Image = React.createClass({
182186
RawImage = RCTImageView;
183187
}
184188

185-
return (
186-
<RawImage
187-
{...this.props}
188-
style={style}
189-
resizeMode={resizeMode}
190-
tintColor={tintColor}
191-
src={source.uri}
192-
defaultImageSrc={defaultSource.uri}
193-
/>
194-
);
189+
if (this.context.isInAParentText) {
190+
return <RCTVirtualImage source={source}/>;
191+
} else {
192+
return (
193+
<RawImage
194+
{...this.props}
195+
style={style}
196+
resizeMode={resizeMode}
197+
tintColor={tintColor}
198+
src={source.uri}
199+
defaultImageSrc={defaultSource.uri}
200+
/>
201+
);
202+
}
195203
}
196204
});
197205

@@ -210,6 +218,7 @@ var cfg = {
210218
},
211219
};
212220
var RCTImageView = requireNativeComponent('RCTImageView', Image, cfg);
213-
var RCTNetworkImageView = (NativeModules.NetworkImageViewManager) ? requireNativeComponent('RCTNetworkImageView', Image, cfg) : RCTImageView;
221+
var RCTNetworkImageView = NativeModules.NetworkImageViewManager ? requireNativeComponent('RCTNetworkImageView', Image, cfg) : RCTImageView;
222+
var RCTVirtualImage = requireNativeComponent('RCTVirtualImage', Image);
214223

215224
module.exports = Image;

Libraries/Image/RCTGIFImageDecoder.m

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
4848

4949
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
5050
if (!image) {
51-
image = [UIImage imageWithCGImage:imageRef];
51+
image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
5252
}
5353

5454
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
@@ -98,7 +98,7 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
9898
// Don't bother creating an animation
9999
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
100100
if (imageRef) {
101-
image = [UIImage imageWithCGImage:imageRef];
101+
image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
102102
CFRelease(imageRef);
103103
}
104104
CFRelease(imageSource);

Libraries/Image/RCTImage.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
1212
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; };
1313
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
14+
13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */; settings = {ASSET_TAGS = (); }; };
15+
13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */; settings = {ASSET_TAGS = (); }; };
1416
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
1517
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
1618
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
@@ -39,6 +41,10 @@
3941
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = "<group>"; };
4042
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = "<group>"; };
4143
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = "<group>"; };
44+
13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowVirtualImage.h; sourceTree = "<group>"; };
45+
13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowVirtualImage.m; sourceTree = "<group>"; };
46+
13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualImageManager.h; sourceTree = "<group>"; };
47+
13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualImageManager.m; sourceTree = "<group>"; };
4248
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = "<group>"; };
4349
143879371AAD32A300F088A5 /* RCTImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoader.m; sourceTree = "<group>"; };
4450
35123E691B59C99D00EBAD80 /* RCTImageStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageStoreManager.h; sourceTree = "<group>"; };
@@ -84,6 +90,10 @@
8490
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */,
8591
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
8692
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
93+
13EF7F071BC42D4E003F47DD /* RCTShadowVirtualImage.h */,
94+
13EF7F081BC42D4E003F47DD /* RCTShadowVirtualImage.m */,
95+
13EF7F091BC42D4E003F47DD /* RCTVirtualImageManager.h */,
96+
13EF7F0A1BC42D4E003F47DD /* RCTVirtualImageManager.m */,
8797
58B5115E1A9E6B3D00147676 /* Products */,
8898
);
8999
indentWidth = 2;
@@ -154,13 +164,15 @@
154164
isa = PBXSourcesBuildPhase;
155165
buildActionMask = 2147483647;
156166
files = (
167+
13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */,
157168
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
158169
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
159170
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
160171
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
161172
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
162173
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */,
163174
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
175+
13EF7F0B1BC42D4E003F47DD /* RCTShadowVirtualImage.m in Sources */,
164176
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */,
165177
83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */,
166178
);

Libraries/Image/RCTImageDownloader.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
135135
// Normally -dataWithContentsOfURL: would be bad but this is a data URL.
136136
NSData *data = [NSData dataWithContentsOfURL:imageURL];
137137

138-
UIImage *image = [UIImage imageWithData:data];
138+
UIImage *image = [UIImage imageWithData:data scale:scale];
139139
if (image) {
140140
if (progressHandler) {
141141
progressHandler(1, 1);

Libraries/Image/RCTImageLoader.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#import "RCTConvert.h"
1515
#import "RCTDefines.h"
1616
#import "RCTImageDownloader.h"
17+
#import "RCTImageUtils.h"
1718
#import "RCTLog.h"
1819
#import "RCTUtils.h"
1920

@@ -165,7 +166,7 @@ - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
165166
}];
166167
} else {
167168
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
168-
UIImage *image = [UIImage imageWithData:data];
169+
UIImage *image = [UIImage imageWithData:data scale:scale];
169170
if (image) {
170171
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
171172
} else {

Libraries/Image/RCTImageView.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
*/
99

1010
#import <UIKit/UIKit.h>
11+
#import "RCTImageComponent.h"
1112

1213
@class RCTBridge;
1314

14-
@interface RCTImageView : UIImageView
15+
@interface RCTImageView : UIImageView <RCTImageComponent>
1516

1617
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
1718

Libraries/Image/RCTImageView.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
* Determines whether an image of `currentSize` should be reloaded for display
2323
* at `idealSize`.
2424
*/
25-
static BOOL RCTShouldReloadImageForSizeChange(CGSize currentSize, CGSize idealSize) {
25+
static BOOL RCTShouldReloadImageForSizeChange(CGSize currentSize, CGSize idealSize)
26+
{
2627
static const CGFloat upscaleThreshold = 1.2;
2728
static const CGFloat downscaleThreshold = 0.5;
2829

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import "RCTShadowView.h"
11+
#import "RCTImageComponent.h"
12+
13+
@class RCTBridge;
14+
15+
/**
16+
* Shadow image component, used for embedding images in non-view contexts such
17+
* as text. This is NOT used for ordinary <Image> views.
18+
*/
19+
@interface RCTShadowVirtualImage : RCTShadowView <RCTImageComponent>
20+
21+
- (instancetype)initWithBridge:(RCTBridge *)bridge;
22+
23+
@property (nonatomic, copy) NSDictionary *source;
24+
25+
@end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
10+
#import "RCTShadowVirtualImage.h"
11+
#import "RCTImageLoader.h"
12+
#import "RCTBridge.h"
13+
#import "RCTConvert.h"
14+
15+
@implementation RCTShadowVirtualImage
16+
{
17+
RCTBridge *_bridge;
18+
}
19+
20+
@synthesize image = _image;
21+
22+
- (instancetype)initWithBridge:(RCTBridge *)bridge
23+
{
24+
if ((self = [super init])) {
25+
_bridge = bridge;
26+
}
27+
return self;
28+
}
29+
30+
RCT_NOT_IMPLEMENTED(-(instancetype)init)
31+
32+
- (void)setSource:(NSDictionary *)source
33+
{
34+
if (![source isEqual:_source]) {
35+
_source = [source copy];
36+
NSString *imageTag = [RCTConvert NSString:_source[@"uri"]];
37+
CGFloat scale = [RCTConvert CGFloat:_source[@"scale"]] ?: 1;
38+
39+
__weak RCTShadowVirtualImage *weakSelf = self;
40+
[_bridge.imageLoader loadImageWithTag:imageTag size:CGSizeZero scale:scale resizeMode:UIViewContentModeScaleToFill progressBlock:nil completionBlock:^(NSError *error, UIImage *image) {
41+
RCTShadowVirtualImage *strongSelf = weakSelf;
42+
strongSelf->_image = image;
43+
[strongSelf dirtyText];
44+
}];
45+
}
46+
}
47+
48+
@end

0 commit comments

Comments
 (0)