iOS: Add a maxFontSizeMultiplier prop to <Text> and <TextInput> (#20915)
Summary: **Motivation** Whenever a user changes the system font size to its maximum allowable setting, React Native apps that allow font scaling can become unusable because the text gets too big. Experimenting with a native app like iMessage on iOS, the font size used for non-body text (e.g. header, navigational elements) is capped while the body text (e.g. text in the message bubbles) is allowed to grow. This PR introduces a new prop on `<Text>` and `<TextInput>` called `maxFontSizeMultiplier`. This enables devs to set the maximum allowed text scale factor on a Text/TextInput. The default is 0 which means no limit. Another PR will add this feature to Android. **Test Plan** I created a test app which utilizes all categories of values of `maxFontSizeMultiplier`: - `undefined`: inherit from parent - `0`: no limit - `1`, `1.2`: fixed limits I tried this with `Text`, `TextInput` with `value`, and `TextInput` with children. For `Text`, I also verified that nesting works properly (if a child `Text` doesn't specify `maxFontSizeMultiplier`, it inherits it from its parent). Lastly, we've been using a version of this in Skype for several months. **Release Notes** [GENERAL] [ENHANCEMENT] [Text/TextInput] - Added maxFontSizeMultiplier prop to prevent some text from getting unusably large as user increases OS's font scale setting (iOS) Adam Comella Microsoft Corp. Pull Request resolved: https://github.com/facebook/react-native/pull/20915 Differential Revision: D9646739 Pulled By: shergin fbshipit-source-id: c823f59c1e342c22d6297b88b2cb11c5a1f10310
This commit is contained in:
parent
fed5b6e27a
commit
01d5eff425
|
@ -177,6 +177,7 @@ type Props = $ReadOnly<{|
|
||||||
autoCorrect?: ?boolean,
|
autoCorrect?: ?boolean,
|
||||||
autoFocus?: ?boolean,
|
autoFocus?: ?boolean,
|
||||||
allowFontScaling?: ?boolean,
|
allowFontScaling?: ?boolean,
|
||||||
|
maxFontSizeMultiplier?: ?boolean,
|
||||||
editable?: ?boolean,
|
editable?: ?boolean,
|
||||||
keyboardType?: ?KeyboardType,
|
keyboardType?: ?KeyboardType,
|
||||||
returnKeyType?: ?ReturnKeyType,
|
returnKeyType?: ?ReturnKeyType,
|
||||||
|
@ -367,6 +368,14 @@ const TextInput = createReactClass({
|
||||||
* default is `true`.
|
* default is `true`.
|
||||||
*/
|
*/
|
||||||
allowFontScaling: PropTypes.bool,
|
allowFontScaling: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Specifies largest possible scale a font can reach when `allowFontScaling` is enabled.
|
||||||
|
* Possible values:
|
||||||
|
* `null/undefined` (default): inherit from the parent node or the global default (0)
|
||||||
|
* `0`: no max, ignore parent/global default
|
||||||
|
* `>= 1`: sets the maxFontSizeMultiplier of this node to this value
|
||||||
|
*/
|
||||||
|
maxFontSizeMultiplier: PropTypes.number,
|
||||||
/**
|
/**
|
||||||
* If `false`, text is not editable. The default value is `true`.
|
* If `false`, text is not editable. The default value is `true`.
|
||||||
*/
|
*/
|
||||||
|
@ -933,7 +942,11 @@ const TextInput = createReactClass({
|
||||||
);
|
);
|
||||||
if (childCount >= 1) {
|
if (childCount >= 1) {
|
||||||
children = (
|
children = (
|
||||||
<Text style={props.style} allowFontScaling={props.allowFontScaling}>
|
<Text
|
||||||
|
style={props.style}
|
||||||
|
allowFontScaling={props.allowFontScaling}
|
||||||
|
maxFontSizeMultiplier={props.maxFontSizeMultiplier}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,6 +36,7 @@ RCT_REMAP_SHADOW_PROPERTY(fontWeight, textAttributes.fontWeight, NSString)
|
||||||
RCT_REMAP_SHADOW_PROPERTY(fontStyle, textAttributes.fontStyle, NSString)
|
RCT_REMAP_SHADOW_PROPERTY(fontStyle, textAttributes.fontStyle, NSString)
|
||||||
RCT_REMAP_SHADOW_PROPERTY(fontVariant, textAttributes.fontVariant, NSArray)
|
RCT_REMAP_SHADOW_PROPERTY(fontVariant, textAttributes.fontVariant, NSArray)
|
||||||
RCT_REMAP_SHADOW_PROPERTY(allowFontScaling, textAttributes.allowFontScaling, BOOL)
|
RCT_REMAP_SHADOW_PROPERTY(allowFontScaling, textAttributes.allowFontScaling, BOOL)
|
||||||
|
RCT_REMAP_SHADOW_PROPERTY(maxFontSizeMultiplier, textAttributes.maxFontSizeMultiplier, CGFloat)
|
||||||
RCT_REMAP_SHADOW_PROPERTY(letterSpacing, textAttributes.letterSpacing, CGFloat)
|
RCT_REMAP_SHADOW_PROPERTY(letterSpacing, textAttributes.letterSpacing, CGFloat)
|
||||||
// Paragraph Styles
|
// Paragraph Styles
|
||||||
RCT_REMAP_SHADOW_PROPERTY(lineHeight, textAttributes.lineHeight, CGFloat)
|
RCT_REMAP_SHADOW_PROPERTY(lineHeight, textAttributes.lineHeight, CGFloat)
|
||||||
|
|
|
@ -31,6 +31,7 @@ extern NSString *const RCTTextAttributesTagAttributeName;
|
||||||
@property (nonatomic, copy, nullable) NSString *fontFamily;
|
@property (nonatomic, copy, nullable) NSString *fontFamily;
|
||||||
@property (nonatomic, assign) CGFloat fontSize;
|
@property (nonatomic, assign) CGFloat fontSize;
|
||||||
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
|
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
|
||||||
|
@property (nonatomic, assign) CGFloat maxFontSizeMultiplier;
|
||||||
@property (nonatomic, copy, nullable) NSString *fontWeight;
|
@property (nonatomic, copy, nullable) NSString *fontWeight;
|
||||||
@property (nonatomic, copy, nullable) NSString *fontStyle;
|
@property (nonatomic, copy, nullable) NSString *fontStyle;
|
||||||
@property (nonatomic, copy, nullable) NSArray<NSString *> *fontVariant;
|
@property (nonatomic, copy, nullable) NSArray<NSString *> *fontVariant;
|
||||||
|
@ -71,7 +72,7 @@ extern NSString *const RCTTextAttributesTagAttributeName;
|
||||||
- (UIFont *)effectiveFont;
|
- (UIFont *)effectiveFont;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Font size multiplier reflects `allowFontScaling` and `fontSizeMultiplier`.
|
* Font size multiplier reflects `allowFontScaling`, `fontSizeMultiplier`, and `maxFontSizeMultiplier`.
|
||||||
*/
|
*/
|
||||||
- (CGFloat)effectiveFontSizeMultiplier;
|
- (CGFloat)effectiveFontSizeMultiplier;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
||||||
_lineHeight = NAN;
|
_lineHeight = NAN;
|
||||||
_textDecorationStyle = NSUnderlineStyleSingle;
|
_textDecorationStyle = NSUnderlineStyleSingle;
|
||||||
_fontSizeMultiplier = NAN;
|
_fontSizeMultiplier = NAN;
|
||||||
|
_maxFontSizeMultiplier = NAN;
|
||||||
_alignment = NSTextAlignmentNatural;
|
_alignment = NSTextAlignmentNatural;
|
||||||
_baseWritingDirection = NSWritingDirectionNatural;
|
_baseWritingDirection = NSWritingDirectionNatural;
|
||||||
_textShadowRadius = NAN;
|
_textShadowRadius = NAN;
|
||||||
|
@ -49,6 +50,7 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
||||||
_fontFamily = textAttributes->_fontFamily ?: _fontFamily;
|
_fontFamily = textAttributes->_fontFamily ?: _fontFamily;
|
||||||
_fontSize = !isnan(textAttributes->_fontSize) ? textAttributes->_fontSize : _fontSize;
|
_fontSize = !isnan(textAttributes->_fontSize) ? textAttributes->_fontSize : _fontSize;
|
||||||
_fontSizeMultiplier = !isnan(textAttributes->_fontSizeMultiplier) ? textAttributes->_fontSizeMultiplier : _fontSizeMultiplier;
|
_fontSizeMultiplier = !isnan(textAttributes->_fontSizeMultiplier) ? textAttributes->_fontSizeMultiplier : _fontSizeMultiplier;
|
||||||
|
_maxFontSizeMultiplier = !isnan(textAttributes->_maxFontSizeMultiplier) ? textAttributes->_maxFontSizeMultiplier : _maxFontSizeMultiplier;
|
||||||
_fontWeight = textAttributes->_fontWeight ?: _fontWeight;
|
_fontWeight = textAttributes->_fontWeight ?: _fontWeight;
|
||||||
_fontStyle = textAttributes->_fontStyle ?: _fontStyle;
|
_fontStyle = textAttributes->_fontStyle ?: _fontStyle;
|
||||||
_fontVariant = textAttributes->_fontVariant ?: _fontVariant;
|
_fontVariant = textAttributes->_fontVariant ?: _fontVariant;
|
||||||
|
@ -191,7 +193,15 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
||||||
|
|
||||||
- (CGFloat)effectiveFontSizeMultiplier
|
- (CGFloat)effectiveFontSizeMultiplier
|
||||||
{
|
{
|
||||||
return !RCTHasFontHandlerSet() && _allowFontScaling && !isnan(_fontSizeMultiplier) ? _fontSizeMultiplier : 1.0;
|
bool fontScalingEnabled = !RCTHasFontHandlerSet() && _allowFontScaling;
|
||||||
|
|
||||||
|
if (fontScalingEnabled) {
|
||||||
|
CGFloat fontSizeMultiplier = !isnan(_fontSizeMultiplier) ? _fontSizeMultiplier : 1.0;
|
||||||
|
CGFloat maxFontSizeMultiplier = !isnan(_maxFontSizeMultiplier) ? _maxFontSizeMultiplier : 0.0;
|
||||||
|
return maxFontSizeMultiplier >= 1.0 ? fminf(maxFontSizeMultiplier, fontSizeMultiplier) : fontSizeMultiplier;
|
||||||
|
} else {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (UIColor *)effectiveForegroundColor
|
- (UIColor *)effectiveForegroundColor
|
||||||
|
@ -260,6 +270,7 @@ NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttrib
|
||||||
RCTTextAttributesCompareObjects(_fontFamily) &&
|
RCTTextAttributesCompareObjects(_fontFamily) &&
|
||||||
RCTTextAttributesCompareFloats(_fontSize) &&
|
RCTTextAttributesCompareFloats(_fontSize) &&
|
||||||
RCTTextAttributesCompareFloats(_fontSizeMultiplier) &&
|
RCTTextAttributesCompareFloats(_fontSizeMultiplier) &&
|
||||||
|
RCTTextAttributesCompareFloats(_maxFontSizeMultiplier) &&
|
||||||
RCTTextAttributesCompareStrings(_fontWeight) &&
|
RCTTextAttributesCompareStrings(_fontWeight) &&
|
||||||
RCTTextAttributesCompareObjects(_fontStyle) &&
|
RCTTextAttributesCompareObjects(_fontStyle) &&
|
||||||
RCTTextAttributesCompareObjects(_fontVariant) &&
|
RCTTextAttributesCompareObjects(_fontVariant) &&
|
||||||
|
|
|
@ -58,6 +58,7 @@ const viewConfig = {
|
||||||
numberOfLines: true,
|
numberOfLines: true,
|
||||||
ellipsizeMode: true,
|
ellipsizeMode: true,
|
||||||
allowFontScaling: true,
|
allowFontScaling: true,
|
||||||
|
maxFontSizeMultiplier: true,
|
||||||
disabled: true,
|
disabled: true,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
selectionColor: true,
|
selectionColor: true,
|
||||||
|
@ -263,6 +264,7 @@ const RCTVirtualText =
|
||||||
validAttributes: {
|
validAttributes: {
|
||||||
...ReactNativeViewAttributes.UIView,
|
...ReactNativeViewAttributes.UIView,
|
||||||
isHighlighted: true,
|
isHighlighted: true,
|
||||||
|
maxFontSizeMultiplier: true,
|
||||||
},
|
},
|
||||||
uiViewClassName: 'RCTVirtualText',
|
uiViewClassName: 'RCTVirtualText',
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
@implementation RCTTextViewManager
|
@implementation RCTTextViewManager
|
||||||
{
|
{
|
||||||
NSHashTable<RCTTextShadowView *> *_shadowViews;
|
NSHashTable<RCTTextShadowView *> *_shadowViews;
|
||||||
CGFloat _fontSizeMultiplier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_MODULE(RCTText)
|
RCT_EXPORT_MODULE(RCTText)
|
||||||
|
|
|
@ -100,6 +100,14 @@ module.exports = {
|
||||||
* See https://facebook.github.io/react-native/docs/text.html#allowfontscaling
|
* See https://facebook.github.io/react-native/docs/text.html#allowfontscaling
|
||||||
*/
|
*/
|
||||||
allowFontScaling: PropTypes.bool,
|
allowFontScaling: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* Specifies largest possible scale a font can reach when `allowFontScaling` is enabled.
|
||||||
|
* Possible values:
|
||||||
|
* `null/undefined` (default): inherit from the parent node or the global default (0)
|
||||||
|
* `0`: no max, ignore parent/global default
|
||||||
|
* `>= 1`: sets the maxFontSizeMultiplier of this node to this value
|
||||||
|
*/
|
||||||
|
maxFontSizeMultiplier: PropTypes.number,
|
||||||
/**
|
/**
|
||||||
* Indicates whether the view is an accessibility element.
|
* Indicates whether the view is an accessibility element.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue