Skip to content

Commit 53fb5b6

Browse files
author
Vladislav Alexeev
committed
Dynamic Text Sizes for Text component
Summary: Dynamic Text Sizes for Text component. Text gains new prop - allowFontScaling (false by default). There is also AccessibilityManager module that allows you to tune multipliers per each content size category.
1 parent 407eb4c commit 53fb5b6

File tree

14 files changed

+331
-13
lines changed

14 files changed

+331
-13
lines changed

Examples/UIExplorer/TextExample.ios.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,25 @@ exports.examples = [
378378
</View>
379379
);
380380
},
381+
}, {
382+
title: 'allowFontScaling attribute',
383+
render: function() {
384+
return (
385+
<View>
386+
<Text>
387+
By default, text will respect Text Size accessibility setting on iOS.
388+
It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
389+
{" "}<Text style={{fontWeight: 'bold'}}>Settings.app - Display & Brightness - Text Size</Text>
390+
</Text>
391+
<Text style={{marginTop: 10}}>
392+
You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
393+
</Text>
394+
<Text allowFontScaling={false} style={{marginTop: 20}}>
395+
This text will not scale.
396+
</Text>
397+
</View>
398+
);
399+
},
381400
}];
382401

383402
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: 49 additions & 5 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];
@@ -232,8 +252,9 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib
232252
[attributedString enumerateAttribute:NSParagraphStyleAttributeName inRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
233253
if (value) {
234254
NSParagraphStyle *paragraphStyle = (NSParagraphStyle *)value;
235-
if ([paragraphStyle maximumLineHeight] > _lineHeight) {
236-
self.lineHeight = [paragraphStyle maximumLineHeight];
255+
CGFloat maximumLineHeight = round([paragraphStyle maximumLineHeight] / self.fontSizeMultiplier);
256+
if (maximumLineHeight > self.lineHeight) {
257+
self.lineHeight = maximumLineHeight;
237258
}
238259
hasParagraphStyle = YES;
239260
}
@@ -247,8 +268,9 @@ - (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attrib
247268
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
248269
paragraphStyle.alignment = _textAlign;
249270
paragraphStyle.baseWritingDirection = _writingDirection;
250-
paragraphStyle.minimumLineHeight = _lineHeight;
251-
paragraphStyle.maximumLineHeight = _lineHeight;
271+
CGFloat lineHeight = round(_lineHeight * self.fontSizeMultiplier);
272+
paragraphStyle.minimumLineHeight = lineHeight;
273+
paragraphStyle.maximumLineHeight = lineHeight;
252274
[attributedString addAttribute:NSParagraphStyleAttributeName
253275
value:paragraphStyle
254276
range:(NSRange){0, attributedString.length}];
@@ -321,4 +343,26 @@ - (void)set##setProp:(type)value; \
321343
RCT_TEXT_PROPERTY(TextDecorationStyle, _textDecorationStyle, NSUnderlineStyle);
322344
RCT_TEXT_PROPERTY(WritingDirection, _writingDirection, NSWritingDirection)
323345

346+
- (void)setAllowFontScaling:(BOOL)allowFontScaling
347+
{
348+
_allowFontScaling = allowFontScaling;
349+
for (RCTShadowView *child in [self reactSubviews]) {
350+
if ([child isKindOfClass:[RCTShadowText class]]) {
351+
[(RCTShadowText *)child setAllowFontScaling:allowFontScaling];
352+
}
353+
}
354+
[self dirtyText];
355+
}
356+
357+
- (void)setFontSizeMultiplier:(CGFloat)fontSizeMultiplier
358+
{
359+
_fontSizeMultiplier = fontSizeMultiplier;
360+
for (RCTShadowView *child in [self reactSubviews]) {
361+
if ([child isKindOfClass:[RCTShadowText class]]) {
362+
[(RCTShadowText *)child setFontSizeMultiplier:fontSizeMultiplier];
363+
}
364+
}
365+
[self dirtyText];
366+
}
367+
324368
@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: 12 additions & 1 deletion
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,15 +100,25 @@ var Text = React.createClass({
99100
* Used to locate this view in end-to-end tests.
100101
*/
101102
testID: React.PropTypes.string,
103+
/**
104+
* Specifies should fonts scale to respect Text Size accessibility setting on iOS.
105+
*/
106+
allowFontScaling: React.PropTypes.bool,
102107
},
103108

104109
viewConfig: viewConfig,
105110

106-
getInitialState: function() {
111+
getInitialState: function(): Object {
107112
return merge(this.touchableGetInitialState(), {
108113
isHighlighted: false,
109114
});
110115
},
116+
117+
getDefaultProps: function(): Object {
118+
return {
119+
allowFontScaling: true,
120+
};
121+
},
111122

112123
onStartShouldSetResponder: function(): bool {
113124
var shouldSetFromProps = this.props.onStartShouldSetResponder &&

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)