diff --git a/Examples/UIExplorer/TextExample.ios.js b/Examples/UIExplorer/TextExample.ios.js
index 4889bc472..10323c7a5 100644
--- a/Examples/UIExplorer/TextExample.ios.js
+++ b/Examples/UIExplorer/TextExample.ios.js
@@ -218,6 +218,26 @@ exports.examples = [
);
},
+}, {
+ title: 'Letter Spacing',
+ render: function() {
+ return (
+
+
+ letterSpacing = 0
+
+
+ letterSpacing = 2
+
+
+ letterSpacing = 9
+
+
+ letterSpacing = -1
+
+
+ );
+ },
}, {
title: 'Spaces',
render: function() {
diff --git a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png
index 2c6675b7b..edde59bcc 100644
Binary files a/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png and b/Examples/UIExplorer/UIExplorerTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp/testTextExampleSnapshot_1@2x.png differ
diff --git a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m
index df748ef03..974e3281c 100644
--- a/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m
+++ b/Examples/UIExplorer/UIExplorerTests/UIExplorerTests.m
@@ -88,35 +88,19 @@
XCTAssertTrue(foundElement, @"Cound't find element with '' text in %d seconds", TIMEOUT_SECONDS);
}
-- (void)testViewExampleSnapshot
-{
- [_runner runTest:_cmd module:@"ViewExample"];
+#define RCT_SNAPSHOT_TEST(name, reRecord) \
+- (void)test##name##Snapshot \
+{ \
+ _runner.recordMode |= reRecord; \
+ [_runner runTest:_cmd module:@#name]; \
}
-- (void)testLayoutExampleSnapshot
-{
- [_runner runTest:_cmd module:@"LayoutExample"];
-}
-
-- (void)testTextExampleSnapshot
-{
- [_runner runTest:_cmd module:@"TextExample"];
-}
-
-- (void)testSwitchExampleSnapshot
-{
- [_runner runTest:_cmd module:@"SwitchExample"];
-}
-
-- (void)testSliderExampleSnapshot
-{
- [_runner runTest:_cmd module:@"SliderExample"];
-}
-
-- (void)testTabBarExampleSnapshot
-{
- [_runner runTest:_cmd module:@"TabBarExample"];
-}
+RCT_SNAPSHOT_TEST(ViewExample, NO)
+RCT_SNAPSHOT_TEST(LayoutExample, NO)
+RCT_SNAPSHOT_TEST(TextExample, NO)
+RCT_SNAPSHOT_TEST(SwitchExample, NO)
+RCT_SNAPSHOT_TEST(SliderExample, NO)
+RCT_SNAPSHOT_TEST(TabBarExample, NO)
// Make sure this test runs last
- (void)testZZZ_NotInRecordMode
diff --git a/Libraries/Text/RCTShadowText.h b/Libraries/Text/RCTShadowText.h
index 286edb53a..7a7b44cf8 100644
--- a/Libraries/Text/RCTShadowText.h
+++ b/Libraries/Text/RCTShadowText.h
@@ -22,6 +22,7 @@ extern NSString *const RCTReactTagAttributeName;
@property (nonatomic, copy) NSString *fontWeight;
@property (nonatomic, copy) NSString *fontStyle;
@property (nonatomic, assign) BOOL isHighlighted;
+@property (nonatomic, assign) CGFloat letterSpacing;
@property (nonatomic, assign) CGFloat lineHeight;
@property (nonatomic, assign) NSUInteger maximumNumberOfLines;
@property (nonatomic, assign) CGSize shadowOffset;
@@ -30,6 +31,7 @@ extern NSString *const RCTReactTagAttributeName;
// Not exposed to JS
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSLineBreakMode truncationMode;
+@property (nonatomic, assign) CGFloat effectiveLetterSpacing;
@property (nonatomic, copy, readonly) NSAttributedString *attributedString;
@property (nonatomic, strong, readonly) NSLayoutManager *layoutManager;
diff --git a/Libraries/Text/RCTShadowText.m b/Libraries/Text/RCTShadowText.m
index 4ea9d3bd6..b7e6f997e 100644
--- a/Libraries/Text/RCTShadowText.m
+++ b/Libraries/Text/RCTShadowText.m
@@ -40,11 +40,15 @@ static css_dim_t RCTMeasure(void *context, float width)
css_dim_t result;
result.dimensions[CSS_WIDTH] = RCTCeilPixelValue(computedSize.width);
+ if (shadowText.effectiveLetterSpacing < 0) {
+ result.dimensions[CSS_WIDTH] -= shadowText.effectiveLetterSpacing;
+ }
result.dimensions[CSS_HEIGHT] = RCTCeilPixelValue(computedSize.height);
return result;
}
-@implementation RCTShadowText {
+@implementation RCTShadowText
+{
NSLayoutManager *_layoutManager;
NSTextContainer *_textContainer;
NSAttributedString *_cachedAttributedString;
@@ -55,6 +59,7 @@ static css_dim_t RCTMeasure(void *context, float width)
{
if ((self = [super init])) {
_fontSize = NAN;
+ _letterSpacing = NAN;
_isHighlighted = NO;
_textContainer = [[NSTextContainer alloc] init];
@@ -71,22 +76,24 @@ static css_dim_t RCTMeasure(void *context, float width)
- (NSAttributedString *)attributedString
{
return [self _attributedStringWithFontFamily:nil
- fontSize:0
+ fontSize:nil
fontWeight:nil
- fontStyle:nil];
+ fontStyle:nil
+ letterSpacing:nil];
}
- (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
- fontSize:(CGFloat)fontSize
+ fontSize:(NSNumber *)fontSize
fontWeight:(NSString *)fontWeight
fontStyle:(NSString *)fontStyle
+ letterSpacing:(NSNumber *)letterSpacing
{
if (![self isTextDirty] && _cachedAttributedString) {
return _cachedAttributedString;
}
if (_fontSize && !isnan(_fontSize)) {
- fontSize = _fontSize;
+ fontSize = @(_fontSize);
}
if (_fontWeight) {
fontWeight = _fontWeight;
@@ -97,12 +104,17 @@ static css_dim_t RCTMeasure(void *context, float width)
if (_fontFamily) {
fontFamily = _fontFamily;
}
+ if (!isnan(_letterSpacing)) {
+ letterSpacing = @(_letterSpacing);
+ }
+
+ _effectiveLetterSpacing = letterSpacing.doubleValue;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
for (RCTShadowView *child in [self reactSubviews]) {
if ([child isKindOfClass:[RCTShadowText class]]) {
RCTShadowText *shadowText = (RCTShadowText *)child;
- [attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight fontStyle:fontStyle]];
+ [attributedString appendAttributedString:[shadowText _attributedStringWithFontFamily:fontFamily fontSize:fontSize fontWeight:fontWeight fontStyle:fontStyle letterSpacing:letterSpacing]];
} else if ([child isKindOfClass:[RCTShadowRawText class]]) {
RCTShadowRawText *shadowRawText = (RCTShadowRawText *)child;
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[shadowRawText text] ?: @""]];
@@ -123,8 +135,9 @@ static css_dim_t RCTMeasure(void *context, float width)
[self _addAttribute:NSBackgroundColorAttributeName withValue:self.textBackgroundColor toAttributedString:attributedString];
}
- _font = [RCTConvert UIFont:nil withFamily:fontFamily size:@(fontSize) weight:fontWeight style:fontStyle];
+ _font = [RCTConvert UIFont:nil withFamily:fontFamily size:fontSize weight:fontWeight style:fontStyle];
[self _addAttribute:NSFontAttributeName withValue:_font toAttributedString:attributedString];
+ [self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString];
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
[self _setParagraphStyleOnAttributedString:attributedString];
@@ -143,7 +156,7 @@ static css_dim_t RCTMeasure(void *context, float width)
- (void)_addAttribute:(NSString *)attribute withValue:(id)attributeValue toAttributedString:(NSMutableAttributedString *)attributedString
{
[attributedString enumerateAttribute:attribute inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
- if (!value) {
+ if (!value && attributeValue) {
[attributedString addAttribute:attribute value:attributeValue range:range];
}
}];
@@ -223,6 +236,7 @@ RCT_TEXT_PROPERTY(Color, _color, UIColor *);
RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *);
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat);
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *);
+RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat);
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat);
RCT_TEXT_PROPERTY(ShadowOffset, _shadowOffset, CGSize);
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment);
diff --git a/Libraries/Text/RCTText.h b/Libraries/Text/RCTText.h
index 24b98e991..fe7803eb5 100644
--- a/Libraries/Text/RCTText.h
+++ b/Libraries/Text/RCTText.h
@@ -14,7 +14,6 @@
@property (nonatomic, strong) NSLayoutManager *layoutManager;
@property (nonatomic, strong) NSTextContainer *textContainer;
@property (nonatomic, copy) NSAttributedString *attributedText;
-
@property (nonatomic, assign) UIEdgeInsets contentInset;
@end
diff --git a/Libraries/Text/RCTTextManager.m b/Libraries/Text/RCTTextManager.m
index ef518d204..b2832a800 100644
--- a/Libraries/Text/RCTTextManager.m
+++ b/Libraries/Text/RCTTextManager.m
@@ -45,6 +45,7 @@ RCT_EXPORT_SHADOW_PROPERTY(fontSize, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(fontWeight, NSString)
RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString)
RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
+RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
RCT_EXPORT_SHADOW_PROPERTY(maximumNumberOfLines, NSInteger)
RCT_EXPORT_SHADOW_PROPERTY(shadowOffset, CGSize)
diff --git a/Libraries/Text/TextStylePropTypes.js b/Libraries/Text/TextStylePropTypes.js
index 632bc9e9d..95a0f4cb8 100644
--- a/Libraries/Text/TextStylePropTypes.js
+++ b/Libraries/Text/TextStylePropTypes.js
@@ -32,6 +32,7 @@ var TextStylePropTypes = Object.assign(Object.create(ViewStylePropTypes), {
writingDirection: ReactPropTypes.oneOf(
['auto' /*default*/, 'ltr', 'rtl']
),
+ letterSpacing: ReactPropTypes.number,
});
// Text doesn't support padding correctly (#4841912)