Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Support allowFontScaling on TextInput #14030

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ const TextInput = createReactClass({
* @platform android
*/
autoGrow: PropTypes.bool,
/**
* Specifies whether fonts should scale to respect Text Size accessibility settings. The
* default is `true`.
*/
allowFontScaling: PropTypes.bool,
/**
* If `false`, text is not editable. The default value is `true`.
*/
Expand Down Expand Up @@ -550,7 +555,11 @@ const TextInput = createReactClass({
*/
caretHidden: PropTypes.bool,
},

getDefaultProps(): Object {
return {
allowFontScaling: true,
};
},
/**
* `NativeMethodsMixin` will look for this when invoking `setNativeProps`. We
* make `this` look like an actual native component class.
Expand Down Expand Up @@ -688,7 +697,7 @@ const TextInput = createReactClass({
'Cannot specify both value and children.'
);
if (childCount >= 1) {
children = <Text style={props.style}>{children}</Text>;
children = <Text style={props.style} allowFontScaling={props.allowFontScaling}>{children}</Text>;
}
if (props.inputView) {
children = [children, props.inputView];
Expand Down
31 changes: 31 additions & 0 deletions Libraries/Text/RCTFontAttributes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <UIKit/UIKit.h>

#import "RCTFontAttributesDelegate.h"

@class RCTAccessibilityManager;

@interface RCTFontAttributes : NSObject

@property (nonatomic, weak) id<RCTFontAttributesDelegate> delegate;

@property (readonly, nonatomic, strong) UIFont *font;

@property (nonatomic, assign) BOOL allowFontScaling;
@property (nonatomic, copy) NSString *fontFamily;
@property (nonatomic, strong) NSNumber *fontSize;
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
@property (nonatomic, copy) NSString *fontStyle;
@property (nonatomic, copy) NSString *fontWeight;

- (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessibilityManager;

@end
112 changes: 112 additions & 0 deletions Libraries/Text/RCTFontAttributes.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "RCTFontAttributes.h"

#import <React/RCTAccessibilityManager.h>
#import <React/RCTAssert.h>
#import <React/RCTFont.h>
#import <React/RCTLog.h>

@interface RCTFontAttributes ()
{
RCTAccessibilityManager *_accessibilityManager;
}

@property (nonatomic, strong) UIFont *font;

@end

@implementation RCTFontAttributes

- (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessibilityManager
{
RCTAssertParam(accessibilityManager);

if (self = [super init]) {
_accessibilityManager = accessibilityManager;
_fontSizeMultiplier = _accessibilityManager.multiplier;

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(contentSizeMultiplierDidChange)
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
object:_accessibilityManager];

[self updateFont];
}
return self;
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)contentSizeMultiplierDidChange
{
self.fontSizeMultiplier = _accessibilityManager.multiplier;
}

- (void)setAllowFontScaling:(BOOL)allowFontScaling
{
_allowFontScaling = allowFontScaling;
[self updateFont];
}

- (void)setFontFamily:(NSString *)fontFamily
{
_fontFamily = fontFamily;
[self updateFont];
}

- (void)setFontSize:(NSNumber *)fontSize
{
_fontSize = fontSize;
[self updateFont];
}

- (void)setFontSizeMultiplier:(CGFloat)fontSizeMultiplier
{
_fontSizeMultiplier = fontSizeMultiplier;

if (_fontSizeMultiplier == 0) {
RCTLogError(@"fontSizeMultiplier value must be > zero.");
_fontSizeMultiplier = 1.0;
}

[self updateFont];
}

- (void)setFontStyle:(NSString *)fontStyle
{
_fontStyle = fontStyle;
[self updateFont];
}

- (void)setFontWeight:(NSString *)fontWeight
{
_fontWeight = fontWeight;
[self updateFont];
}

- (void)updateFont
{
CGFloat scaleMultiplier = self.allowFontScaling ? self.fontSizeMultiplier : 1.0;
self.font = [RCTFont updateFont:nil
withFamily:self.fontFamily
size:self.fontSize
weight:self.fontWeight
style:self.fontStyle
variant:nil
scaleMultiplier:scaleMultiplier];

[self.delegate fontAttributesDidChangeWithFont:self.font];
}

@end
14 changes: 14 additions & 0 deletions Libraries/Text/RCTFontAttributesDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

@protocol RCTFontAttributesDelegate <NSObject>

- (void)fontAttributesDidChangeWithFont:(UIFont *)font;

@end
8 changes: 8 additions & 0 deletions Libraries/Text/RCTText.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
59F60E921E661BDD0081153B /* RCTShadowTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */; };
59F60E931E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; };
59F60E941E661BDD0081153B /* RCTShadowTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59F60E901E661BDD0081153B /* RCTShadowTextView.m */; };
A85C829A1F742AA20036C019 /* RCTFontAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = A85C82991F742AA20036C019 /* RCTFontAttributes.m */; };
AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; };
AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -111,6 +112,9 @@
59F60E8E1E661BDD0081153B /* RCTShadowTextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextField.m; sourceTree = "<group>"; };
59F60E8F1E661BDD0081153B /* RCTShadowTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTShadowTextView.h; sourceTree = "<group>"; };
59F60E901E661BDD0081153B /* RCTShadowTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowTextView.m; sourceTree = "<group>"; };
A85C82981F742AA20036C019 /* RCTFontAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributes.h; sourceTree = "<group>"; };
A85C82991F742AA20036C019 /* RCTFontAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFontAttributes.m; sourceTree = "<group>"; };
A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributesDelegate.h; sourceTree = "<group>"; };
AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+Text.h"; sourceTree = "<group>"; };
AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand All @@ -126,6 +130,9 @@
599DF25D1F0304B30079B53E /* RCTBackedTextInputViewProtocol.h */,
AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */,
AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */,
A85C82981F742AA20036C019 /* RCTFontAttributes.h */,
A85C82991F742AA20036C019 /* RCTFontAttributes.m */,
A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */,
58B511C61A9E6C5C00147676 /* RCTRawTextManager.h */,
58B511C71A9E6C5C00147676 /* RCTRawTextManager.m */,
58B511C81A9E6C5C00147676 /* RCTShadowRawText.h */,
Expand Down Expand Up @@ -278,6 +285,7 @@
1362F1001B4D51F400E06D8C /* RCTTextField.m in Sources */,
59AF89AA1EDCBCC700F004B1 /* RCTUITextField.m in Sources */,
598F41261F145D4900B8495B /* RCTBackedTextInputDelegateAdapter.m in Sources */,
A85C829A1F742AA20036C019 /* RCTFontAttributes.m in Sources */,
58B512161A9E6EFF00147676 /* RCTText.m in Sources */,
599DF2641F03076D0079B53E /* RCTTextInput.m in Sources */,
1362F1011B4D51F400E06D8C /* RCTTextFieldManager.m in Sources */,
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Text/RCTTextField.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTFont.h>
#import <React/RCTUIManager.h>
#import <React/RCTUtils.h>
#import <React/UIView+React.h>
Expand Down Expand Up @@ -41,6 +42,8 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
_backedTextInput.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_backedTextInput.textInputDelegate = self;

self.font = self.fontAttributes.font;

[self addSubview:_backedTextInput];
}

Expand Down
22 changes: 6 additions & 16 deletions Libraries/Text/RCTTextFieldManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,16 @@ - (UIView *)view

#pragma mark - Unified <TextInput> properties

RCT_REMAP_VIEW_PROPERTY(allowFontScaling, fontAttributes.allowFontScaling, BOOL)
RCT_REMAP_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType)
RCT_REMAP_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType)
RCT_REMAP_VIEW_PROPERTY(color, backedTextInputView.textColor, UIColor)
RCT_REMAP_VIEW_PROPERTY(editable, backedTextInputView.editable, BOOL)
RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL)
RCT_REMAP_VIEW_PROPERTY(fontSize, fontAttributes.fontSize, NSNumber)
RCT_REMAP_VIEW_PROPERTY(fontWeight, fontAttributes.fontWeight, NSString)
RCT_REMAP_VIEW_PROPERTY(fontStyle, fontAttributes.fontStyle, NSString)
RCT_REMAP_VIEW_PROPERTY(fontFamily, fontAttributes.fontFamily, NSString)
RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance)
RCT_REMAP_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType)
RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString)
Expand All @@ -61,22 +66,7 @@ - (UIView *)view
RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL)
RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode)
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, RCTTextField)
{
view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withSize:json ?: @(defaultView.backedTextInputView.font.pointSize)];
}
RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused RCTTextField)
{
view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withWeight:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused RCTTextField)
{
view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withStyle:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTTextField)
{
view.backedTextInputView.font = [RCTFont updateFont:view.backedTextInputView.font withFamily:json ?: defaultView.backedTextInputView.font.familyName];
}

RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger)

- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
Expand Down
8 changes: 7 additions & 1 deletion Libraries/Text/RCTTextInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
#import <React/RCTView.h>

#import "RCTBackedTextInputViewProtocol.h"
#import "RCTFontAttributes.h"
#import "RCTFontAttributesDelegate.h"

@class RCTBridge;
@class RCTEventDispatcher;
@class RCTTextSelection;

@interface RCTTextInput : RCTView {
@interface RCTTextInput : RCTView <RCTFontAttributesDelegate> {
@protected
RCTBridge *_bridge;
RCTEventDispatcher *_eventDispatcher;
Expand All @@ -41,12 +43,16 @@
@property (nonatomic, copy) RCTDirectEventBlock onContentSizeChange;
@property (nonatomic, copy) RCTDirectEventBlock onSelectionChange;

@property (nonatomic, readonly, strong) RCTFontAttributes *fontAttributes;

@property (nonatomic, assign) NSInteger mostRecentEventCount;
@property (nonatomic, assign) BOOL blurOnSubmit;
@property (nonatomic, assign) BOOL selectTextOnFocus;
@property (nonatomic, assign) BOOL clearTextOnFocus;
@property (nonatomic, copy) RCTTextSelection *selection;

- (void)setFont:(UIFont *)font;

- (void)invalidateContentSize;

// Temporary exposure of particial `RCTBackedTextInputDelegate` support.
Expand Down
13 changes: 13 additions & 0 deletions Libraries/Text/RCTTextInput.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#import "RCTTextInput.h"

#import <React/RCTAccessibilityManager.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
Expand All @@ -29,6 +30,8 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
if (self = [super initWithFrame:CGRectZero]) {
_bridge = bridge;
_eventDispatcher = bridge.eventDispatcher;
_fontAttributes = [[RCTFontAttributes alloc] initWithAccessibilityManager:bridge.accessibilityManager];
_fontAttributes.delegate = self;
}

return self;
Expand All @@ -44,6 +47,16 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
return nil;
}

- (void)setFont:(UIFont *)font
{
self.backedTextInputView.font = font;
}

- (void)fontAttributesDidChangeWithFont:(UIFont *)font
{
self.font = font;
}

#pragma mark - Properties

- (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets
Expand Down
15 changes: 15 additions & 0 deletions Libraries/Text/RCTTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTFont.h>
#import <React/RCTUIManager.h>
#import <React/RCTUtils.h>
#import <React/UIView+React.h>
Expand Down Expand Up @@ -57,6 +58,8 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge

_backedTextInput.textInputDelegate = self;

self.font = self.fontAttributes.font;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It ensures that _backedTextInput's font gets initialized to RCTFontAttributes's default font. This is not necessarily the same as _backedTextInput's default font.


[self addSubview:_backedTextInput];
}
return self;
Expand All @@ -70,6 +73,18 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge
return _backedTextInput;
}

- (void)fontAttributesDidChangeWithFont:(UIFont *)font
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(We can consider moving this to superclass.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overrides the superclass's implementation of fontAttributesDidChangeWithFont. Are you suggesting I move this logic to the superclass's implementation so RCTTextView doesn't have to override it?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. invalidateContentSize does not exist in base class, right? I think we can also move this method to the base class (even with possibly empty implementation).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This does not block landing. We can do it in the future.)

{
[super fontAttributesDidChangeWithFont:font];

// Because the font changed, the TextInput may take up more space now so we call
// invalidateContentSize. However, if there's currently no text to render, then
// there's no need to call invalidateContentSize.
if ([self text].length != 0) {
[self invalidateContentSize];
}
}

#pragma mark - RCTComponent

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index
Expand Down
Loading