diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.m b/Libraries/Text/TextInput/Multiline/RCTUITextView.m index df7e26dc2dd..774f8a0b23d 100644 --- a/Libraries/Text/TextInput/Multiline/RCTUITextView.m +++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.m @@ -18,10 +18,9 @@ UILabel *_placeholderView; UITextView *_detachedTextView; RCTBackedTextViewDelegateAdapter *_textInputDelegateAdapter; + NSDictionary *_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 *)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 *)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 *)placeholderEffectiveTextAttributes +- (void)_updatePlaceholder { - NSMutableDictionary *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 *)_placeholderTextAttributes +{ + NSMutableDictionary *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 diff --git a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h index f2fb4f3c5e1..0a6184c7980 100644 --- a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h +++ b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h @@ -25,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong, nullable) UIView *inputAccessoryView; @property (nonatomic, weak, nullable) id textInputDelegate; @property (nonatomic, readonly) CGSize contentSize; -@property (nonatomic, strong, nullable) RCTTextAttributes *reactTextAttributes; +@property (nonatomic, strong, nullable) NSDictionary *defaultTextAttributes; // This protocol disallows direct access to `selectedTextRange` property because // unwise usage of it can break the `delegate` behavior. So, we always have to diff --git a/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m b/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m index aac20ea3c69..9dda9c36c19 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputShadowView.m @@ -47,6 +47,16 @@ return YES; } +- (void)didSetProps:(NSArray *)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. diff --git a/Libraries/Text/TextInput/RCTBaseTextInputView.m b/Libraries/Text/TextInput/RCTBaseTextInputView.m index 283c3213976..d970649e571 100644 --- a/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -67,7 +67,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) - (void)enforceTextAttributesIfNeeded { id backedTextInputView = self.backedTextInputView; - backedTextInputView.reactTextAttributes = _textAttributes; + backedTextInputView.defaultTextAttributes = [_textAttributes effectiveTextAttributes]; } - (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets diff --git a/Libraries/Text/TextInput/Singleline/RCTUITextField.m b/Libraries/Text/TextInput/Singleline/RCTUITextField.m index 10e3d3df7a1..96a4e11869e 100644 --- a/Libraries/Text/TextInput/Singleline/RCTUITextField.m +++ b/Libraries/Text/TextInput/Singleline/RCTUITextField.m @@ -15,10 +15,9 @@ @implementation RCTUITextField { RCTBackedTextFieldDelegateAdapter *_textInputDelegateAdapter; + NSDictionary *_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 *)defaultTextAttributes { - if ([reactTextAttributes isEqual:_reactTextAttributes]) { - return; - } - self.defaultTextAttributes = reactTextAttributes.effectiveTextAttributes; - _reactTextAttributes = reactTextAttributes; + _defaultTextAttributes = defaultTextAttributes; + [super setDefaultTextAttributes:defaultTextAttributes]; [self _updatePlaceholder]; } -- (RCTTextAttributes *)reactTextAttributes +- (NSDictionary *)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 *)placeholderEffectiveTextAttributes +- (NSDictionary *)_placeholderTextAttributes { - NSMutableDictionary *effectiveTextAttributes = [NSMutableDictionary dictionary]; - - if (_placeholderColor) { - effectiveTextAttributes[NSForegroundColorAttributeName] = _placeholderColor; + NSMutableDictionary *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 ``. 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;