Skip to content

Commit be2cabc

Browse files
author
Vladislav Alexeev
committed
Dynamic Text Sizes for Text component
Summary: Dynamic Text Sizes for Text component. Text gains new prop - allowFontScaling (true by default). There is also AccessibilityManager module that allows you to tune multipliers per each content size category, but predefined multipliers are there. This could potentially break some apps so please test carefully.
1 parent eb06659 commit be2cabc

14 files changed

Lines changed: 331 additions & 12 deletions

Examples/UIExplorer/TextExample.ios.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,25 @@ exports.examples = [
369369
</View>
370370
);
371371
},
372+
}, {
373+
title: 'allowFontScaling attribute',
374+
render: function() {
375+
return (
376+
<View>
377+
<Text>
378+
By default, text will respect Text Size accessibility setting on iOS.
379+
It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
380+
{" "}<Text style={{fontWeight: 'bold'}}>Settings.app - Display & Brightness - Text Size</Text>
381+
</Text>
382+
<Text style={{marginTop: 10}}>
383+
You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
384+
</Text>
385+
<Text allowFontScaling={false} style={{marginTop: 20}}>
386+
This text will not scale.
387+
</Text>
388+
</View>
389+
);
390+
},
372391
}];
373392

374393
var styles = StyleSheet.create({

Libraries/ART/RCTConvert+ART.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ + (ARTTextFrame)ARTTextFrame:(id)json
8787
}
8888

8989
NSDictionary *fontDict = dict[@"font"];
90-
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"]];
90+
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
9191
if (!font) {
9292
return frame;
9393
}

Libraries/Text/RCTShadowRawText.m

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,32 @@
99

1010
#import "RCTShadowRawText.h"
1111

12+
#import "RCTUIManager.h"
13+
1214
@implementation RCTShadowRawText
1315

16+
- (instancetype)init
17+
{
18+
if ((self = [super init])) {
19+
[[NSNotificationCenter defaultCenter] addObserver:self
20+
selector:@selector(contentSizeMultiplierDidChange:)
21+
name:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification
22+
object:nil];
23+
}
24+
return self;
25+
}
26+
27+
- (void)dealloc
28+
{
29+
[[NSNotificationCenter defaultCenter] removeObserver:self];
30+
}
31+
32+
- (void)contentSizeMultiplierDidChange:(NSNotification *)note
33+
{
34+
[self dirtyLayout];
35+
[self dirtyText];
36+
}
37+
1438
- (void)setText:(NSString *)text
1539
{
1640
if (_text != text) {

Libraries/Text/RCTShadowText.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ extern NSString *const RCTReactTagAttributeName;
3030
@property (nonatomic, strong) UIColor *textDecorationColor;
3131
@property (nonatomic, assign) NSUnderlineStyle textDecorationStyle;
3232
@property (nonatomic, assign) RCTTextDecorationLineType textDecorationLine;
33+
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
34+
@property (nonatomic, assign) BOOL allowFontScaling;
3335

3436
- (void)recomputeText;
3537

Libraries/Text/RCTShadowText.m

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
#import "RCTShadowText.h"
1111

12+
#import "RCTAccessibilityManager.h"
13+
#import "RCTUIManager.h"
14+
#import "RCTBridge.h"
1215
#import "RCTConvert.h"
1316
#import "RCTLog.h"
1417
#import "RCTShadowRawText.h"
@@ -51,16 +54,31 @@ - (instancetype)init
5154
_letterSpacing = NAN;
5255
_isHighlighted = NO;
5356
_textDecorationStyle = NSUnderlineStyleSingle;
57+
[[NSNotificationCenter defaultCenter] addObserver:self
58+
selector:@selector(contentSizeMultiplierDidChange:)
59+
name:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification
60+
object:nil];
5461
}
5562
return self;
5663
}
5764

65+
- (void)dealloc
66+
{
67+
[[NSNotificationCenter defaultCenter] removeObserver:self];
68+
}
69+
5870
- (NSString *)description
5971
{
6072
NSString *superDescription = super.description;
6173
return [[superDescription substringToIndex:superDescription.length - 1] stringByAppendingFormat:@"; text: %@>", [self attributedString].string];
6274
}
6375

76+
- (void)contentSizeMultiplierDidChange:(NSNotification *)note
77+
{
78+
[self dirtyLayout];
79+
[self dirtyText];
80+
}
81+
6482
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
6583
parentProperties:(NSDictionary *)parentProperties
6684
{
@@ -190,7 +208,9 @@ - (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
190208
[self _addAttribute:NSBackgroundColorAttributeName withValue:self.backgroundColor toAttributedString:attributedString];
191209
}
192210

193-
UIFont *font = [RCTConvert UIFont:nil withFamily:fontFamily size:fontSize weight:fontWeight style:fontStyle];
211+
UIFont *font = [RCTConvert UIFont:nil withFamily:fontFamily
212+
size:fontSize weight:fontWeight style:fontStyle
213+
scaleMultiplier:(_allowFontScaling && _fontSizeMultiplier > 0.0 ? _fontSizeMultiplier : 1.0)];
194214
[self _addAttribute:NSFontAttributeName withValue:font toAttributedString:attributedString];
195215
[self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString];
196216
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
@@ -247,8 +267,9 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib
247267
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
248268
paragraphStyle.alignment = _textAlign;
249269
paragraphStyle.baseWritingDirection = _writingDirection;
250-
paragraphStyle.minimumLineHeight = _lineHeight;
251-
paragraphStyle.maximumLineHeight = _lineHeight;
270+
CGFloat lineHeight = round(_lineHeight * self.fontSizeMultiplier);
271+
paragraphStyle.minimumLineHeight = lineHeight;
272+
paragraphStyle.maximumLineHeight = lineHeight;
252273
[attributedString addAttribute:NSParagraphStyleAttributeName
253274
value:paragraphStyle
254275
range:(NSRange){0, attributedString.length}];
@@ -321,4 +342,26 @@ - (void)set##setProp:(type)value; \
321342
RCT_TEXT_PROPERTY(TextDecorationStyle, _textDecorationStyle, NSUnderlineStyle);
322343
RCT_TEXT_PROPERTY(WritingDirection, _writingDirection, NSWritingDirection)
323344

345+
- (void)setAllowFontScaling:(BOOL)allowFontScaling
346+
{
347+
_allowFontScaling = allowFontScaling;
348+
for (RCTShadowView *child in [self reactSubviews]) {
349+
if ([child isKindOfClass:[RCTShadowText class]]) {
350+
[(RCTShadowText *)child setAllowFontScaling:allowFontScaling];
351+
}
352+
}
353+
[self dirtyText];
354+
}
355+
356+
- (void)setFontSizeMultiplier:(CGFloat)fontSizeMultiplier
357+
{
358+
_fontSizeMultiplier = fontSizeMultiplier;
359+
for (RCTShadowView *child in [self reactSubviews]) {
360+
if ([child isKindOfClass:[RCTShadowText class]]) {
361+
[(RCTShadowText *)child setFontSizeMultiplier:fontSizeMultiplier];
362+
}
363+
}
364+
[self dirtyText];
365+
}
366+
324367
@end

Libraries/Text/RCTTextManager.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#import "RCTTextManager.h"
1111

12+
#import "RCTAccessibilityManager.h"
1213
#import "RCTAssert.h"
1314
#import "RCTConvert.h"
1415
#import "RCTLog.h"
@@ -49,6 +50,7 @@ - (RCTShadowView *)shadowView
4950
RCT_EXPORT_SHADOW_PROPERTY(textDecorationColor, UIColor)
5051
RCT_EXPORT_SHADOW_PROPERTY(textDecorationLine, RCTTextDecorationLineType)
5152
RCT_EXPORT_SHADOW_PROPERTY(writingDirection, NSWritingDirection)
53+
RCT_EXPORT_SHADOW_PROPERTY(allowFontScaling, BOOL)
5254

5355
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
5456
{
@@ -69,6 +71,7 @@ - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)
6971
RCTAssert([shadowView isTextDirty], @"Don't process any nodes that don't have dirty text");
7072

7173
if ([shadowView isKindOfClass:[RCTShadowText class]]) {
74+
[(RCTShadowText *)shadowView setFontSizeMultiplier:self.bridge.accessibilityManager.multiplier];
7275
[(RCTShadowText *)shadowView recomputeText];
7376
} else if ([shadowView isKindOfClass:[RCTShadowRawText class]]) {
7477
RCTLogError(@"Raw text cannot be used outside of a <Text> tag. Not rendering string: '%@'",

Libraries/Text/Text.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ var viewConfig = {
3030
validAttributes: merge(ReactNativeViewAttributes.UIView, {
3131
isHighlighted: true,
3232
numberOfLines: true,
33+
allowFontScaling: true,
3334
}),
3435
uiViewClassName: 'RCTText',
3536
};
@@ -99,16 +100,27 @@ var Text = React.createClass({
99100
*
100101
* {nativeEvent: {layout: {x, y, width, height}}}.
101102
*/
102-
onLayout: React.PropTypes.func,
103+
onLayout: React.PropTypes.func,
104+
/**
105+
* Specifies should fonts scale to respect Text Size accessibility setting.
106+
*/
107+
allowFontScaling: React.PropTypes.bool,
103108
},
104109

105110
viewConfig: viewConfig,
106111

107-
getInitialState: function() {
112+
getInitialState: function(): Object {
108113
return merge(this.touchableGetInitialState(), {
109114
isHighlighted: false,
110115
});
111116
},
117+
118+
getDefaultProps: function(): Object {
119+
return {
120+
numberOfLines: 0,
121+
allowFontScaling: true,
122+
};
123+
},
112124

113125
onStartShouldSetResponder: function(): bool {
114126
var shouldSetFromProps = this.props.onStartShouldSetResponder &&
@@ -231,6 +243,7 @@ if (Platform.OS === 'android') {
231243
RCTVirtualText = createReactNativeComponentClass({
232244
validAttributes: merge(ReactNativeViewAttributes.UIView, {
233245
isHighlighted: true,
246+
allowFontScaling: false,
234247
}),
235248
uiViewClassName: 'RCTVirtualText',
236249
});

React/Base/RCTConvert.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ typedef NSURL RCTFileURL;
9191
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json;
9292
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json;
9393
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family
94-
size:(id)size weight:(id)weight style:(id)style;
94+
size:(id)size weight:(id)weight style:(id)style
95+
scaleMultiplier:(CGFloat)scaleMultiplier;
9596

9697
typedef NSArray NSStringArray;
9798
+ (NSStringArray *)NSStringArray:(id)json;

React/Base/RCTConvert.m

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -779,31 +779,33 @@ + (UIFont *)UIFont:(id)json
779779
withFamily:json[@"fontFamily"]
780780
size:json[@"fontSize"]
781781
weight:json[@"fontWeight"]
782-
style:json[@"fontStyle"]];
782+
style:json[@"fontStyle"]
783+
scaleMultiplier:1.0f];
783784
}
784785

785786
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json
786787
{
787-
return [self UIFont:font withFamily:nil size:json weight:nil style:nil];
788+
return [self UIFont:font withFamily:nil size:json weight:nil style:nil scaleMultiplier:1.0];
788789
}
789790

790791
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json
791792
{
792-
return [self UIFont:font withFamily:nil size:nil weight:json style:nil];
793+
return [self UIFont:font withFamily:nil size:nil weight:json style:nil scaleMultiplier:1.0];
793794
}
794795

795796
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json
796797
{
797-
return [self UIFont:font withFamily:nil size:nil weight:nil style:json];
798+
return [self UIFont:font withFamily:nil size:nil weight:nil style:json scaleMultiplier:1.0];
798799
}
799800

800801
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json
801802
{
802-
return [self UIFont:font withFamily:json size:nil weight:nil style:nil];
803+
return [self UIFont:font withFamily:json size:nil weight:nil style:nil scaleMultiplier:1.0];
803804
}
804805

805806
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family
806807
size:(id)size weight:(id)weight style:(id)style
808+
scaleMultiplier:(CGFloat)scaleMultiplier
807809
{
808810
// Defaults
809811
NSString *const RCTDefaultFontFamily = @"System";
@@ -828,6 +830,9 @@ + (UIFont *)UIFont:(UIFont *)font withFamily:(id)family
828830

829831
// Get font attributes
830832
fontSize = [self CGFloat:size] ?: fontSize;
833+
if (scaleMultiplier > 0.0 && scaleMultiplier != 1.0) {
834+
fontSize = round(fontSize * scaleMultiplier);
835+
}
831836
familyName = [self NSString:family] ?: familyName;
832837
isItalic = style ? [self RCTFontStyle:style] : isItalic;
833838
fontWeight = weight ? [self RCTFontWeight:weight] : fontWeight;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 <Foundation/Foundation.h>
11+
12+
#import "RCTBridgeModule.h"
13+
#import "RCTBridge.h"
14+
15+
extern NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification; // posted when multiplier is changed
16+
17+
@interface RCTAccessibilityManager : NSObject <RCTBridgeModule>
18+
19+
@property (nonatomic, readonly) CGFloat multiplier;
20+
21+
@end
22+
23+
@interface RCTBridge (RCTAccessibilityManager)
24+
25+
@property (nonatomic, readonly) RCTAccessibilityManager *accessibilityManager;
26+
27+
@end

0 commit comments

Comments
 (0)