Permalink
Browse files

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
  • Loading branch information...
shergin authored and facebook-github-bot committed Jul 18, 2017
1 parent f5d9b52 commit 603cc48ceba001827d10231d51b4031c7768eef8
@@ -22,6 +22,7 @@
@property (nonatomic, assign) UIEdgeInsets textContainerInset;
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
@property (nonatomic, weak, nullable) id<RCTBackedTextInputDelegate> 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
@@ -168,10 +168,10 @@ - (void)textInputDidEndEditing
- (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;
}
@@ -137,6 +137,12 @@ - (void)paste:(id)sender
#pragma mark - Layout
- (CGSize)contentSize
{
// Returning size DOES contain `textContainerInset` (aka `padding`).
return self.intrinsicContentSize;
}
- (CGSize)intrinsicContentSize
{
// Note: `placeholder` defines intrinsic size for `<TextInput>`.
@@ -145,11 +151,13 @@ - (CGSize)intrinsicContentSize
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));
}
@@ -152,6 +152,7 @@ - (void)setContentOffset:(CGPoint)contentOffset animated:(__unused BOOL)animated
- (CGFloat)preferredMaxLayoutWidth
{
// Returning size DOES contain `textContainerInset` (aka `padding`).
return _preferredMaxLayoutWidth ?: self.placeholderSize.width;
}
@@ -167,6 +168,18 @@ - (CGSize)placeholderSize
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 @@ - (void)layoutSubviews
- (CGSize)intrinsicContentSize
{
// Returning size DOES contain `textContainerInset` (aka `padding`).
return [self sizeThatFits:CGSizeMake(self.preferredMaxLayoutWidth, INFINITY)];
}
@@ -187,7 +201,7 @@ - (CGSize)sizeThatFits:(CGSize)size
// 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));
}
@@ -823,6 +823,24 @@ exports.examples = [
placeholder="Placeholder defines intrinsic size"
/>
</View>
<View>
<TextInput
style={{
fontSize: 16,
backgroundColor: '#eeeeee',
borderColor: '#666666',
borderWidth: 5,
borderTopWidth: 20,
borderRadius: 10,
borderBottomRightRadius: 20,
padding: 10,
paddingTop: 20,
}}
testID="multiline_textinput_with_flex"
multiline={true}
placeholder="Placeholder defines intrinsic size"
/>
</View>
</View>
);
}

4 comments on commit 603cc48

@eliperkins

This comment has been minimized.

Show comment
Hide comment
@eliperkins

eliperkins Aug 8, 2017

Contributor

I think this needs to get cherry picked into 0.47.0-stable to fix issues like #15326. Otherwise, multiline autoexpanding TextInputs will always return the same height for their content.

Contributor

eliperkins replied Aug 8, 2017

I think this needs to get cherry picked into 0.47.0-stable to fix issues like #15326. Otherwise, multiline autoexpanding TextInputs will always return the same height for their content.

@shergin

This comment has been minimized.

Show comment
Hide comment
@shergin

shergin Aug 11, 2017

Contributor

@eliperkins Great! I agree! Could you advocate requesting the cherry peak (probably here: #15318)?

Contributor

shergin replied Aug 11, 2017

@eliperkins Great! I agree! Could you advocate requesting the cherry peak (probably here: #15318)?

@eliperkins

This comment has been minimized.

Show comment
Hide comment
@eliperkins

eliperkins Aug 11, 2017

Contributor

@shergin Done!

Contributor

eliperkins replied Aug 11, 2017

@shergin Done!

@abeltje1

This comment has been minimized.

Show comment
Hide comment
@abeltje1

abeltje1 Aug 16, 2017

Any news on this getting cherry-picked? Would be great!

abeltje1 replied Aug 16, 2017

Any news on this getting cherry-picked? Would be great!

Please sign in to comment.