Proper type for RCTBackedTextInputViewProtocol::defaultTextAttributes

Summary:
This diff changes how we apply default text attributes to backed text input.
The original change in https://github.com/facebook/react-native/pull/23585 that introduced the `reactTextAttributes` field in for RCTBackedTextInputViewProtocol was great! Thank you Wu zhongwuzw !
However, there is one detail that needs to be changed.
RCTBackedTextInputViewProtocol is designed to only abstract complexity of iOS text input components (UITextView and UITextField); it intentionally does not have any React-specific fields or types. Adding a field `RCTTextAttributes *reactTextAttributes;` violates this principle and make it hard to reuse this functionality in the new Fabric-powered TextInput.

This diff changes the type of this prop from `RCTTextAttributes` to `NSDictionary<NSAttributedStringKey,id> *`  (exact same type that UITextView and UITextField use).

Reviewed By: cpojer

Differential Revision: D17408501

fbshipit-source-id: 65f2bba119ccc30f22e87c28d0f8ea6f731cd365
This commit is contained in:
Valentin Shergin
2019-09-17 09:20:54 -07:00
committed by Facebook Github Bot
parent 9833ee7bc1
commit e271fa190d
5 changed files with 57 additions and 66 deletions
@@ -18,10 +18,9 @@
UILabel *_placeholderView;
UITextView *_detachedTextView;
RCTBackedTextViewDelegateAdapter *_textInputDelegateAdapter;
NSDictionary<NSAttributedStringKey, id> *_defaultTextAttributes;
}
@synthesize reactTextAttributes = _reactTextAttributes;
static UIFont *defaultPlaceholderFont()
{
return [UIFont systemFontOfSize:17];
@@ -44,7 +43,6 @@ static UIColor *defaultPlaceholderColor()
_placeholderView = [[UILabel alloc] initWithFrame:self.bounds];
_placeholderView.isAccessibilityElement = NO;
_placeholderView.numberOfLines = 0;
_placeholderView.textColor = defaultPlaceholderColor();
[self addSubview:_placeholderView];
_textInputDelegateAdapter = [[RCTBackedTextViewDelegateAdapter alloc] initWithTextView:self];
@@ -91,35 +89,31 @@ static UIColor *defaultPlaceholderColor()
- (void)setPlaceholder:(NSString *)placeholder
{
_placeholder = placeholder;
_placeholderView.attributedText = [[NSAttributedString alloc] initWithString:_placeholder ?: @"" attributes:[self placeholderEffectiveTextAttributes]];
[self _updatePlaceholder];
}
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
_placeholderColor = placeholderColor;
_placeholderView.textColor = _placeholderColor ?: defaultPlaceholderColor();
[self _updatePlaceholder];
}
- (void)setReactTextAttributes:(RCTTextAttributes *)reactTextAttributes
- (void)setDefaultTextAttributes:(NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes
{
if ([reactTextAttributes isEqual:_reactTextAttributes]) {
return;
}
self.typingAttributes = reactTextAttributes.effectiveTextAttributes;
_reactTextAttributes = reactTextAttributes;
// Update placeholder text attributes
[self setPlaceholder:_placeholder];
_defaultTextAttributes = defaultTextAttributes;
self.typingAttributes = defaultTextAttributes;
[self _updatePlaceholder];
}
- (RCTTextAttributes *)reactTextAttributes
- (NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes
{
return _reactTextAttributes;
return _defaultTextAttributes;
}
- (void)textDidChange
{
_textWasPasted = NO;
[self invalidatePlaceholderVisibility];
[self _invalidatePlaceholderVisibility];
}
#pragma mark - Overrides
@@ -127,7 +121,7 @@ static UIColor *defaultPlaceholderColor()
- (void)setFont:(UIFont *)font
{
[super setFont:font];
_placeholderView.font = font ?: defaultPlaceholderFont();
[self _updatePlaceholder];
}
- (void)setTextAlignment:(NSTextAlignment)textAlignment
@@ -195,7 +189,7 @@ static UIColor *defaultPlaceholderColor()
UIEdgeInsets textContainerInset = self.textContainerInset;
NSString *placeholder = self.placeholder ?: @"";
CGSize maxPlaceholderSize = CGSizeMake(UIEdgeInsetsInsetRect(self.bounds, textContainerInset).size.width, CGFLOAT_MAX);
CGSize placeholderSize = [placeholder boundingRectWithSize:maxPlaceholderSize options:NSStringDrawingUsesLineFragmentOrigin attributes:[self placeholderEffectiveTextAttributes] context:nil].size;
CGSize placeholderSize = [placeholder boundingRectWithSize:maxPlaceholderSize options:NSStringDrawingUsesLineFragmentOrigin attributes:[self _placeholderTextAttributes] context:nil].size;
placeholderSize = CGSizeMake(RCTCeilPixelValue(placeholderSize.width), RCTCeilPixelValue(placeholderSize.height));
placeholderSize.width += textContainerInset.left + textContainerInset.right;
placeholderSize.height += textContainerInset.top + textContainerInset.bottom;
@@ -253,25 +247,28 @@ static UIColor *defaultPlaceholderColor()
#pragma mark - Placeholder
- (void)invalidatePlaceholderVisibility
- (void)_invalidatePlaceholderVisibility
{
BOOL isVisible = _placeholder.length != 0 && self.attributedText.length == 0;
_placeholderView.hidden = !isVisible;
}
- (NSDictionary<NSAttributedStringKey, id> *)placeholderEffectiveTextAttributes
- (void)_updatePlaceholder
{
NSMutableDictionary<NSAttributedStringKey, id> *effectiveTextAttributes = [NSMutableDictionary dictionaryWithDictionary:@{
NSFontAttributeName: _reactTextAttributes.effectiveFont ?: defaultPlaceholderFont(),
NSForegroundColorAttributeName: self.placeholderColor ?: defaultPlaceholderColor(),
NSKernAttributeName:isnan(_reactTextAttributes.letterSpacing) ? @0 : @(_reactTextAttributes.letterSpacing)
}];
NSParagraphStyle *paragraphStyle = [_reactTextAttributes effectiveParagraphStyle];
if (paragraphStyle) {
effectiveTextAttributes[NSParagraphStyleAttributeName] = paragraphStyle;
_placeholderView.attributedText = [[NSAttributedString alloc] initWithString:_placeholder ?: @"" attributes:[self _placeholderTextAttributes]];
}
- (NSDictionary<NSAttributedStringKey, id> *)_placeholderTextAttributes
{
NSMutableDictionary<NSAttributedStringKey, id> *textAttributes = [_defaultTextAttributes mutableCopy] ?: [NSMutableDictionary new];
[textAttributes setValue:self.placeholderColor ?: defaultPlaceholderColor() forKey:NSForegroundColorAttributeName];
if (![textAttributes objectForKey:NSFontAttributeName]) {
[textAttributes setValue:defaultPlaceholderFont() forKey:NSFontAttributeName];
}
return [effectiveTextAttributes copy];
return textAttributes;
}
#pragma mark - Utility Methods
@@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
@property (nonatomic, weak, nullable) id<RCTBackedTextInputDelegate> textInputDelegate;
@property (nonatomic, readonly) CGSize contentSize;
@property (nonatomic, strong, nullable) RCTTextAttributes *reactTextAttributes;
@property (nonatomic, strong, nullable) NSDictionary<NSAttributedStringKey,id> *defaultTextAttributes;
// This protocol disallows direct access to `selectedTextRange` property because
// unwise usage of it can break the `delegate` behavior. So, we always have to
@@ -47,6 +47,16 @@
return YES;
}
- (void)didSetProps:(NSArray<NSString *> *)changedProps
{
[super didSetProps:changedProps];
// `backgroundColor` and `opacity` are being applied directly to a UIView,
// therefore we need to exclude them from base `textAttributes`.
self.textAttributes.backgroundColor = nil;
self.textAttributes.opacity = NAN;
}
- (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext
{
// Do nothing.
@@ -67,7 +67,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
- (void)enforceTextAttributesIfNeeded
{
id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
backedTextInputView.reactTextAttributes = _textAttributes;
backedTextInputView.defaultTextAttributes = [_textAttributes effectiveTextAttributes];
}
- (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets
@@ -15,10 +15,9 @@
@implementation RCTUITextField {
RCTBackedTextFieldDelegateAdapter *_textInputDelegateAdapter;
NSDictionary<NSAttributedStringKey, id> *_defaultTextAttributes;
}
@synthesize reactTextAttributes = _reactTextAttributes;
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
@@ -72,29 +71,22 @@
[self _updatePlaceholder];
}
- (void)setReactTextAttributes:(RCTTextAttributes *)reactTextAttributes
- (void)setDefaultTextAttributes:(NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes
{
if ([reactTextAttributes isEqual:_reactTextAttributes]) {
return;
}
self.defaultTextAttributes = reactTextAttributes.effectiveTextAttributes;
_reactTextAttributes = reactTextAttributes;
_defaultTextAttributes = defaultTextAttributes;
[super setDefaultTextAttributes:defaultTextAttributes];
[self _updatePlaceholder];
}
- (RCTTextAttributes *)reactTextAttributes
- (NSDictionary<NSAttributedStringKey, id> *)defaultTextAttributes
{
return _reactTextAttributes;
return _defaultTextAttributes;
}
- (void)_updatePlaceholder
{
if (self.placeholder == nil) {
return;
}
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder
attributes:[self placeholderEffectiveTextAttributes]];
self.attributedPlaceholder = [[NSAttributedString alloc] initWithString:self.placeholder ?: @""
attributes:[self _placeholderTextAttributes]];
}
- (BOOL)isEditable
@@ -119,24 +111,17 @@
#pragma mark - Placeholder
- (NSDictionary<NSAttributedStringKey, id> *)placeholderEffectiveTextAttributes
- (NSDictionary<NSAttributedStringKey, id> *)_placeholderTextAttributes
{
NSMutableDictionary<NSAttributedStringKey, id> *effectiveTextAttributes = [NSMutableDictionary dictionary];
if (_placeholderColor) {
effectiveTextAttributes[NSForegroundColorAttributeName] = _placeholderColor;
NSMutableDictionary<NSAttributedStringKey, id> *textAttributes = [_defaultTextAttributes mutableCopy] ?: [NSMutableDictionary new];
if (self.placeholderColor) {
[textAttributes setValue:self.placeholderColor forKey:NSForegroundColorAttributeName];
} else {
[textAttributes removeObjectForKey:NSForegroundColorAttributeName];
}
// Kerning
if (!isnan(_reactTextAttributes.letterSpacing)) {
effectiveTextAttributes[NSKernAttributeName] = @(_reactTextAttributes.letterSpacing);
}
NSParagraphStyle *paragraphStyle = [_reactTextAttributes effectiveParagraphStyle];
if (paragraphStyle) {
effectiveTextAttributes[NSParagraphStyleAttributeName] = paragraphStyle;
}
return [effectiveTextAttributes copy];
return textAttributes;
}
#pragma mark - Context Menu
@@ -161,7 +146,6 @@
return [super caretRectForPosition:position];
}
#pragma mark - Positioning Overrides
- (CGRect)textRectForBounds:(CGRect)bounds
@@ -215,7 +199,7 @@
{
// Note: `placeholder` defines intrinsic size for `<TextInput>`.
NSString *text = self.placeholder ?: @"";
CGSize size = [text sizeWithAttributes:[self placeholderEffectiveTextAttributes]];
CGSize size = [text sizeWithAttributes:[self _placeholderTextAttributes]];
size = CGSizeMake(RCTCeilPixelValue(size.width), RCTCeilPixelValue(size.height));
size.width += _textContainerInset.left + _textContainerInset.right;
size.height += _textContainerInset.top + _textContainerInset.bottom;