From 603cc48ceba001827d10231d51b4031c7768eef8 Mon Sep 17 00:00:00 2001 From: Valentin Shergin Date: Tue, 18 Jul 2017 14:33:58 -0700 Subject: [PATCH] TextInput: Refined contentSize calculation Summary: This fixes pretty bad issue when contentSize is calculated based on an intrinsic horizontal (width) limitation, not on a real/current horizontal (width) one. Reviewed By: mmmulani Differential Revision: D5422114 fbshipit-source-id: 0eb582aeb59d29530990d4faabf2f41baa79c058 --- .../Text/RCTBackedTextInputViewProtocol.h | 1 + Libraries/Text/RCTTextInput.m | 8 ++++---- Libraries/Text/RCTUITextField.m | 8 ++++++++ Libraries/Text/RCTUITextView.m | 16 +++++++++++++++- RNTester/js/TextInputExample.ios.js | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/Libraries/Text/RCTBackedTextInputViewProtocol.h b/Libraries/Text/RCTBackedTextInputViewProtocol.h index 79f70ce1b..90e0aa4d6 100644 --- a/Libraries/Text/RCTBackedTextInputViewProtocol.h +++ b/Libraries/Text/RCTBackedTextInputViewProtocol.h @@ -22,6 +22,7 @@ @property (nonatomic, assign) UIEdgeInsets textContainerInset; @property (nonatomic, strong, nullable) UIView *inputAccessoryView; @property (nonatomic, weak, nullable) id textInputDelegate; +@property (nonatomic, readonly) CGSize contentSize; // 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/RCTTextInput.m b/Libraries/Text/RCTTextInput.m index 0927d47ba..4038c54c9 100644 --- a/Libraries/Text/RCTTextInput.m +++ b/Libraries/Text/RCTTextInput.m @@ -168,10 +168,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) - (CGSize)contentSize { - CGSize contentSize = self.intrinsicContentSize; - UIEdgeInsets compoundInsets = self.reactCompoundInsets; - contentSize.width -= compoundInsets.left + compoundInsets.right; - contentSize.height -= compoundInsets.top + compoundInsets.bottom; + CGSize contentSize = self.backedTextInputView.contentSize; + UIEdgeInsets reactPaddingInsets = self.reactPaddingInsets; + contentSize.width -= reactPaddingInsets.left + reactPaddingInsets.right; + contentSize.height -= reactPaddingInsets.top + reactPaddingInsets.bottom; // Returning value does NOT include border and padding insets. return contentSize; } diff --git a/Libraries/Text/RCTUITextField.m b/Libraries/Text/RCTUITextField.m index 82a4b58f5..4b21e594b 100644 --- a/Libraries/Text/RCTUITextField.m +++ b/Libraries/Text/RCTUITextField.m @@ -137,6 +137,12 @@ #pragma mark - Layout +- (CGSize)contentSize +{ + // Returning size DOES contain `textContainerInset` (aka `padding`). + return self.intrinsicContentSize; +} + - (CGSize)intrinsicContentSize { // Note: `placeholder` defines intrinsic size for ``. @@ -145,11 +151,13 @@ size = CGSizeMake(RCTCeilPixelValue(size.width), RCTCeilPixelValue(size.height)); size.width += _textContainerInset.left + _textContainerInset.right; size.height += _textContainerInset.top + _textContainerInset.bottom; + // Returning size DOES contain `textContainerInset` (aka `padding`). return size; } - (CGSize)sizeThatFits:(CGSize)size { + // All size values here contain `textContainerInset` (aka `padding`). CGSize intrinsicSize = self.intrinsicContentSize; return CGSizeMake(MIN(size.width, intrinsicSize.width), MIN(size.height, intrinsicSize.height)); } diff --git a/Libraries/Text/RCTUITextView.m b/Libraries/Text/RCTUITextView.m index f9c3e5d34..45d3cf6df 100644 --- a/Libraries/Text/RCTUITextView.m +++ b/Libraries/Text/RCTUITextView.m @@ -152,6 +152,7 @@ static UIColor *defaultPlaceholderColor() - (CGFloat)preferredMaxLayoutWidth { + // Returning size DOES contain `textContainerInset` (aka `padding`). return _preferredMaxLayoutWidth ?: self.placeholderSize.width; } @@ -167,6 +168,18 @@ static UIColor *defaultPlaceholderColor() return placeholderSize; } +- (CGSize)contentSize +{ + CGSize contentSize = super.contentSize; + CGSize placeholderSize = self.placeholderSize; + // When a text input is empty, it actually displays a placehoder. + // So, we have to consider `placeholderSize` as a minimum `contentSize`. + // Returning size DOES contain `textContainerInset` (aka `padding`). + return CGSizeMake( + MAX(contentSize.width, placeholderSize.width), + MAX(contentSize.height, placeholderSize.height)); +} + - (void)layoutSubviews { [super layoutSubviews]; @@ -179,6 +192,7 @@ static UIColor *defaultPlaceholderColor() - (CGSize)intrinsicContentSize { + // Returning size DOES contain `textContainerInset` (aka `padding`). return [self sizeThatFits:CGSizeMake(self.preferredMaxLayoutWidth, INFINITY)]; } @@ -187,7 +201,7 @@ static UIColor *defaultPlaceholderColor() // Returned fitting size depends on text size and placeholder size. CGSize textSize = [self fixedSizeThatFits:size]; CGSize placeholderSize = self.placeholderSize; - // Returning size DOES contain `textContainerInset`. + // Returning size DOES contain `textContainerInset` (aka `padding`). return CGSizeMake(MAX(textSize.width, placeholderSize.width), MAX(textSize.height, placeholderSize.height)); } diff --git a/RNTester/js/TextInputExample.ios.js b/RNTester/js/TextInputExample.ios.js index 8c2b26534..1a0ee5b15 100644 --- a/RNTester/js/TextInputExample.ios.js +++ b/RNTester/js/TextInputExample.ios.js @@ -823,6 +823,24 @@ exports.examples = [ placeholder="Placeholder defines intrinsic size" /> + + + ); }