iOS textTransform style support
Summary: Issue [#2088](https://github.com/facebook/react-native/issues/2088). The basic desire is to have a declarative mechanism to transform text content to uppercase or lowercase or titlecase ("capitalized"). My test plan involves having added a test-case to the RNTester app within the `<Text>` component area. I then manually verified that the rendered content met my expectation. Here is the markup that exercises my enhancement: ``` <View> <Text style={{ textTransform: 'uppercase'}}> This text should be uppercased. </Text> <Text style={{ textTransform: 'lowercase'}}> This TEXT SHOULD be lowercased. </Text> <Text style={{ textTransform: 'capitalize'}}> This text should be CAPITALIZED. </Text> <Text style={{ textTransform: 'capitalize'}}> Mixed:{' '} <Text style={{ textTransform: 'uppercase'}}> uppercase{' '} </Text> <Text style={{ textTransform: 'lowercase'}}> LoWeRcAsE{' '} </Text> <Text style={{ textTransform: 'capitalize'}}> capitalize each word </Text> </Text> </View> ``` And here is a screenshot of the result: ![screen shot 2018-03-14 at 3 01 02 pm](https://user-images.githubusercontent.com/575821/37433772-7abe7fa0-279a-11e8-9ec9-fb3aa1952dad.png) [Website Documentation PR](https://github.com/facebook/react-native-website/pull/254) https://github.com/facebook/react-native-website/pull/254 [IOS] [ENHANCEMENT] [Text] - added textTransform style property enabling declarative casing transformations Closes https://github.com/facebook/react-native/pull/18387 Differential Revision: D7583315 Pulled By: shergin fbshipit-source-id: a5d22aea2aa4f494b7b25a055abe64799ccbaa79
This commit is contained in:
parent
82bd4337c9
commit
8621d4b797
|
@ -201,6 +201,7 @@ export type ____TextStyle_Internal = $ReadOnly<{|
|
|||
| 'underline line-through',
|
||||
textDecorationStyle?: 'solid' | 'double' | 'dotted' | 'dashed',
|
||||
textDecorationColor?: ColorValue,
|
||||
textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase',
|
||||
writingDirection?: 'auto' | 'ltr' | 'rtl',
|
||||
|}>;
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ NSString *const RCTBaseTextShadowViewEmbeddedShadowViewAttributeName = @"RCTBase
|
|||
NSString *text = rawTextShadowView.text;
|
||||
if (text) {
|
||||
NSAttributedString *rawTextAttributedString =
|
||||
[[NSAttributedString alloc] initWithString:rawTextShadowView.text
|
||||
[[NSAttributedString alloc] initWithString:[textAttributes applyTextAttributesToText:text]
|
||||
attributes:textAttributes.effectiveTextAttributes];
|
||||
[attributedText appendAttributedString:rawTextAttributedString];
|
||||
}
|
||||
|
|
|
@ -51,5 +51,6 @@ RCT_REMAP_SHADOW_PROPERTY(textShadowRadius, textAttributes.textShadowRadius, CGF
|
|||
RCT_REMAP_SHADOW_PROPERTY(textShadowColor, textAttributes.textShadowColor, UIColor)
|
||||
// Special
|
||||
RCT_REMAP_SHADOW_PROPERTY(isHighlighted, textAttributes.isHighlighted, BOOL)
|
||||
RCT_REMAP_SHADOW_PROPERTY(textTransform, textAttributes.textTransform, RCTTextTransform)
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
#import <React/RCTConvert.h>
|
||||
#import <RCTText/RCTTextTransform.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -13,6 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (UITextAutocorrectionType)UITextAutocorrectionType:(nullable id)json;
|
||||
+ (UITextSpellCheckingType)UITextSpellCheckingType:(nullable id)json;
|
||||
+ (RCTTextTransform)RCTTextTransform:(nullable id)json;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -25,4 +25,11 @@
|
|||
UITextSpellCheckingTypeNo;
|
||||
}
|
||||
|
||||
RCT_ENUM_CONVERTER(RCTTextTransform, (@{
|
||||
@"none": @(RCTTextTransformNone),
|
||||
@"capitalize": @(RCTTextTransformCapitalize),
|
||||
@"uppercase": @(RCTTextTransformUppercase),
|
||||
@"lowercase": @(RCTTextTransformLowercase),
|
||||
}), RCTTextTransformUndefined, integerValue)
|
||||
|
||||
@end
|
||||
|
|
|
@ -104,6 +104,8 @@
|
|||
5956B1A7200FF35C008D9D16 /* RCTVirtualTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */; };
|
||||
5956B1A8200FF35C008D9D16 /* RCTVirtualTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12B200FEBAA008D9D16 /* RCTVirtualTextViewManager.m */; };
|
||||
5C245F39205E216A00D936E9 /* RCTInputAccessoryShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C245F37205E216A00D936E9 /* RCTInputAccessoryShadowView.m */; };
|
||||
5970936920845EFC00D163A7 /* RCTTextTransform.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5970936820845DDE00D163A7 /* RCTTextTransform.h */; };
|
||||
5970936A20845F0600D163A7 /* RCTTextTransform.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5970936820845DDE00D163A7 /* RCTTextTransform.h */; };
|
||||
8F2807C7202D2B6B005D65E6 /* RCTInputAccessoryViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2807C1202D2B6A005D65E6 /* RCTInputAccessoryViewManager.m */; };
|
||||
8F2807C8202D2B6B005D65E6 /* RCTInputAccessoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2807C3202D2B6A005D65E6 /* RCTInputAccessoryView.m */; };
|
||||
8F2807C9202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F2807C5202D2B6B005D65E6 /* RCTInputAccessoryViewContent.m */; };
|
||||
|
@ -116,6 +118,7 @@
|
|||
dstPath = include/RCTText;
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
5970936920845EFC00D163A7 /* RCTTextTransform.h in Copy Headers */,
|
||||
5956B160200FF324008D9D16 /* RCTBaseTextShadowView.h in Copy Headers */,
|
||||
5956B161200FF324008D9D16 /* RCTBaseTextViewManager.h in Copy Headers */,
|
||||
5956B162200FF324008D9D16 /* RCTRawTextShadowView.h in Copy Headers */,
|
||||
|
@ -151,6 +154,7 @@
|
|||
dstPath = include/RCTText;
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
5970936A20845F0600D163A7 /* RCTTextTransform.h in Copy Headers */,
|
||||
5956B179200FF338008D9D16 /* RCTBaseTextShadowView.h in Copy Headers */,
|
||||
5956B17A200FF338008D9D16 /* RCTBaseTextViewManager.h in Copy Headers */,
|
||||
5956B17B200FF338008D9D16 /* RCTRawTextShadowView.h in Copy Headers */,
|
||||
|
@ -235,6 +239,7 @@
|
|||
5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = "<group>"; };
|
||||
5C245F37205E216A00D936E9 /* RCTInputAccessoryShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryShadowView.m; sourceTree = "<group>"; };
|
||||
5C245F38205E216A00D936E9 /* RCTInputAccessoryShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryShadowView.h; sourceTree = "<group>"; };
|
||||
5970936820845DDE00D163A7 /* RCTTextTransform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextTransform.h; sourceTree = "<group>"; };
|
||||
8F2807C1202D2B6A005D65E6 /* RCTInputAccessoryViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryViewManager.m; sourceTree = "<group>"; };
|
||||
8F2807C2202D2B6A005D65E6 /* RCTInputAccessoryViewContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTInputAccessoryViewContent.h; sourceTree = "<group>"; };
|
||||
8F2807C3202D2B6A005D65E6 /* RCTInputAccessoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTInputAccessoryView.m; sourceTree = "<group>"; };
|
||||
|
@ -254,6 +259,7 @@
|
|||
5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */,
|
||||
5956B11A200FEBA9008D9D16 /* RCTTextAttributes.h */,
|
||||
5956B120200FEBA9008D9D16 /* RCTTextAttributes.m */,
|
||||
5970936820845DDE00D163A7 /* RCTTextTransform.h */,
|
||||
5956B121200FEBAA008D9D16 /* Text */,
|
||||
5956B0FF200FEBA9008D9D16 /* TextInput */,
|
||||
5956B12A200FEBAA008D9D16 /* VirtualText */,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTTextDecorationLineType.h>
|
||||
#import <RCTText/RCTTextTransform.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -50,6 +51,7 @@ extern NSString *const RCTTextAttributesTagAttributeName;
|
|||
@property (nonatomic, assign) BOOL isHighlighted;
|
||||
@property (nonatomic, strong, nullable) NSNumber *tag;
|
||||
@property (nonatomic, assign) UIUserInterfaceLayoutDirection layoutDirection;
|
||||
@property (nonatomic, assign) RCTTextTransform textTransform;
|
||||
|
||||
#pragma mark - Inheritance
|
||||
|
||||
|
@ -78,6 +80,11 @@ extern NSString *const RCTTextAttributesTagAttributeName;
|
|||
- (UIColor *)effectiveForegroundColor;
|
||||
- (UIColor *)effectiveBackgroundColor;
|
||||
|
||||
/**
|
||||
* Text transformed per 'none', 'uppercase', 'lowercase', 'capitalize'
|
||||
*/
|
||||
- (NSString *)applyTextAttributesToText:(NSString *)text;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -28,6 +28,7 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
|||
_baseWritingDirection = NSWritingDirectionNatural;
|
||||
_textShadowRadius = NAN;
|
||||
_opacity = NAN;
|
||||
_textTransform = RCTTextTransformUndefined;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -73,6 +74,7 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
|||
_isHighlighted = textAttributes->_isHighlighted || _isHighlighted; // *
|
||||
_tag = textAttributes->_tag ?: _tag;
|
||||
_layoutDirection = textAttributes->_layoutDirection != UIUserInterfaceLayoutDirectionLeftToRight ? textAttributes->_layoutDirection : _layoutDirection;
|
||||
_textTransform = textAttributes->_textTransform != RCTTextTransformUndefined ? textAttributes->_textTransform : _textTransform;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSAttributedStringKey, id> *)effectiveTextAttributes
|
||||
|
@ -214,6 +216,21 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
|||
return effectiveBackgroundColor ?: [UIColor clearColor];
|
||||
}
|
||||
|
||||
- (NSString *)applyTextAttributesToText:(NSString *)text
|
||||
{
|
||||
switch (_textTransform) {
|
||||
case RCTTextTransformUndefined:
|
||||
case RCTTextTransformNone:
|
||||
return text;
|
||||
case RCTTextTransformLowercase:
|
||||
return [text lowercaseString];
|
||||
case RCTTextTransformUppercase:
|
||||
return [text uppercaseString];
|
||||
case RCTTextTransformCapitalize:
|
||||
return [text capitalizedString];
|
||||
}
|
||||
}
|
||||
|
||||
- (RCTTextAttributes *)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
RCTTextAttributes *textAttributes = [RCTTextAttributes new];
|
||||
|
@ -263,7 +280,8 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
|||
// Special
|
||||
RCTTextAttributesCompareOthers(_isHighlighted) &&
|
||||
RCTTextAttributesCompareObjects(_tag) &&
|
||||
RCTTextAttributesCompareOthers(_layoutDirection);
|
||||
RCTTextAttributesCompareOthers(_layoutDirection) &&
|
||||
RCTTextAttributesCompareOthers(_textTransform);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTTextTransform) {
|
||||
RCTTextTransformUndefined = 0,
|
||||
RCTTextTransformNone,
|
||||
RCTTextTransformCapitalize,
|
||||
RCTTextTransformUppercase,
|
||||
RCTTextTransformLowercase,
|
||||
};
|
|
@ -84,6 +84,12 @@ const TextStylePropTypes = {
|
|||
* @platform ios
|
||||
*/
|
||||
textDecorationColor: ColorPropType,
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
textTransform: ReactPropTypes.oneOf(
|
||||
['none' /*default*/, 'capitalize', 'uppercase', 'lowercase']
|
||||
),
|
||||
/**
|
||||
* @platform ios
|
||||
*/
|
||||
|
|
|
@ -860,6 +860,42 @@ exports.examples = [
|
|||
title: 'Text `alignItems: \'baseline\'` style',
|
||||
render: function() {
|
||||
return <TextBaseLineLayoutExample />;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Transform',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text style={{ textTransform: 'uppercase'}}>
|
||||
This text should be uppercased.
|
||||
</Text>
|
||||
<Text style={{ textTransform: 'lowercase'}}>
|
||||
This TEXT SHOULD be lowercased.
|
||||
</Text>
|
||||
<Text style={{ textTransform: 'capitalize'}}>
|
||||
This text should be CAPITALIZED.
|
||||
</Text>
|
||||
<Text style={{ textTransform: 'capitalize'}}>
|
||||
Mixed:{' '}
|
||||
<Text style={{ textTransform: 'uppercase'}}>
|
||||
uppercase{' '}
|
||||
</Text>
|
||||
<Text style={{ textTransform: 'lowercase'}}>
|
||||
LoWeRcAsE{' '}
|
||||
</Text>
|
||||
<Text style={{ textTransform: 'capitalize'}}>
|
||||
capitalize each word
|
||||
</Text>
|
||||
</Text>
|
||||
<Text>Should be "ABC":
|
||||
<Text style={{ textTransform: 'uppercase' }}>a<Text>b</Text>c</Text>
|
||||
</Text>
|
||||
<Text>Should be "AbC":
|
||||
<Text style={{ textTransform: 'uppercase' }}>a<Text style={{ textTransform: 'none' }}>b</Text>c</Text>
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
Loading…
Reference in New Issue