Multiline <TextInput> was fixed to match layout logic of singlelined one
Summary: Now padding, border and intinsic sizes are computed same way as for singlelined text input. Reviewed By: mmmulani Differential Revision: D5075880 fbshipit-source-id: 1bc2fd479c13a003c717b1fc3d9c69f4639d4444
This commit is contained in:
parent
4e40521620
commit
48650226e8
|
@ -21,7 +21,6 @@
|
|||
@property (nonatomic, assign) BOOL blurOnSubmit;
|
||||
@property (nonatomic, assign) BOOL clearTextOnFocus;
|
||||
@property (nonatomic, assign) BOOL selectTextOnFocus;
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
|
||||
@property (nonatomic, copy) NSString *text;
|
||||
@property (nonatomic, strong) UIColor *placeholderTextColor;
|
||||
|
@ -30,6 +29,8 @@
|
|||
@property (nonatomic, assign) NSInteger mostRecentEventCount;
|
||||
@property (nonatomic, strong) NSNumber *maxLength;
|
||||
@property (nonatomic, assign, readonly) CGSize contentSize;
|
||||
@property (nonatomic, assign) UIEdgeInsets reactPaddingInsets;
|
||||
@property (nonatomic, assign) UIEdgeInsets reactBorderInsets;
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onChange;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onContentSizeChange;
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
RCTAssertParam(bridge);
|
||||
|
||||
if (self = [super initWithFrame:CGRectZero]) {
|
||||
_contentInset = UIEdgeInsetsZero;
|
||||
_bridge = bridge;
|
||||
_eventDispatcher = bridge.eventDispatcher;
|
||||
_blurOnSubmit = NO;
|
||||
|
@ -203,12 +202,22 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
|
|||
- (void)setFont:(UIFont *)font
|
||||
{
|
||||
_textView.font = font;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setContentInset:(UIEdgeInsets)contentInset
|
||||
- (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets
|
||||
{
|
||||
_contentInset = contentInset;
|
||||
_textView.textContainerInset = contentInset;
|
||||
_reactPaddingInsets = reactPaddingInsets;
|
||||
// We apply `paddingInsets` as `_textView`'s `textContainerInset`.
|
||||
_textView.textContainerInset = reactPaddingInsets;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (void)setReactBorderInsets:(UIEdgeInsets)reactBorderInsets
|
||||
{
|
||||
_reactBorderInsets = reactBorderInsets;
|
||||
// We apply `borderInsets` as `_textView` layout offset.
|
||||
_textView.frame = UIEdgeInsetsInsetRect(self.bounds, reactBorderInsets);
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
|
@ -270,6 +279,7 @@ static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
|
|||
- (void)setPlaceholder:(NSString *)placeholder
|
||||
{
|
||||
_textView.placeholderText = placeholder;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (UIColor *)placeholderTextColor
|
||||
|
@ -541,10 +551,11 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange,
|
|||
|
||||
- (CGSize)contentSize
|
||||
{
|
||||
// Returning value does NOT include insets.
|
||||
// Returning value does NOT include border and padding insets.
|
||||
CGSize contentSize = self.intrinsicContentSize;
|
||||
contentSize.width -= _contentInset.left + _contentInset.right;
|
||||
contentSize.height -= _contentInset.top + _contentInset.bottom;
|
||||
UIEdgeInsets compoundInsets = self.reactCompoundInsets;
|
||||
contentSize.width -= compoundInsets.left + compoundInsets.right;
|
||||
contentSize.height -= compoundInsets.top + compoundInsets.bottom;
|
||||
return contentSize;
|
||||
}
|
||||
|
||||
|
@ -577,13 +588,26 @@ static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange,
|
|||
// Calling `sizeThatFits:` is probably more expensive method to compute
|
||||
// content size compare to direct access `_textView.contentSize` property,
|
||||
// but seems `sizeThatFits:` returns more reliable and consistent result.
|
||||
// Returning value DOES include insets.
|
||||
// Returning value DOES include border and padding insets.
|
||||
return [self sizeThatFits:CGSizeMake(self.bounds.size.width, INFINITY)];
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size
|
||||
{
|
||||
return [_textView sizeThatFits:size];
|
||||
CGFloat compoundHorizontalBorderInset = _reactBorderInsets.left + _reactBorderInsets.right;
|
||||
CGFloat compoundVerticalBorderInset = _reactBorderInsets.top + _reactBorderInsets.bottom;
|
||||
|
||||
size.width -= compoundHorizontalBorderInset;
|
||||
size.height -= compoundVerticalBorderInset;
|
||||
|
||||
// Note: `paddingInsets` already included in `_textView` size
|
||||
// because it was applied as `textContainerInset`.
|
||||
CGSize fittingSize = [_textView sizeThatFits:size];
|
||||
|
||||
fittingSize.width += compoundHorizontalBorderInset;
|
||||
fittingSize.height += compoundVerticalBorderInset;
|
||||
|
||||
return fittingSize;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
|
|
|
@ -83,9 +83,11 @@ RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, textView.dataDetectorTypes, UIDataDet
|
|||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
||||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
UIEdgeInsets borderAsInsets = shadowView.borderAsInsets;
|
||||
UIEdgeInsets paddingAsInsets = shadowView.paddingAsInsets;
|
||||
return ^(RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTTextView *> *viewRegistry) {
|
||||
viewRegistry[reactTag].contentInset = padding;
|
||||
viewRegistry[reactTag].reactBorderInsets = borderAsInsets;
|
||||
viewRegistry[reactTag].reactPaddingInsets = paddingAsInsets;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import "RCTUITextView.h"
|
||||
|
||||
#import <React/UIView+React.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
@implementation RCTUITextView
|
||||
{
|
||||
|
@ -123,6 +124,21 @@ static UIColor *defaultPlaceholderTextColor()
|
|||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size
|
||||
{
|
||||
// Returned fitting size depends on text size and placeholder size.
|
||||
CGSize textSize = [self fixedSizeThatFits:size];
|
||||
|
||||
UIEdgeInsets padddingInsets = self.textContainerInset;
|
||||
NSString *placeholderText = self.placeholderText ?: @"";
|
||||
CGSize placeholderSize = [placeholderText sizeWithAttributes:@{NSFontAttributeName: self.font ?: defaultPlaceholderFont()}];
|
||||
placeholderSize = CGSizeMake(RCTCeilPixelValue(placeholderSize.width), RCTCeilPixelValue(placeholderSize.height));
|
||||
placeholderSize.width += padddingInsets.left + padddingInsets.right;
|
||||
placeholderSize.height += padddingInsets.top + padddingInsets.bottom;
|
||||
|
||||
return CGSizeMake(MAX(textSize.width, placeholderSize.width), MAX(textSize.height, placeholderSize.height));
|
||||
}
|
||||
|
||||
- (CGSize)fixedSizeThatFits:(CGSize)size
|
||||
{
|
||||
// UITextView on iOS 8 has a bug that automatically scrolls to the top
|
||||
// when calling `sizeThatFits:`. Use a copy so that self is not screwed up.
|
||||
|
|
|
@ -763,18 +763,39 @@ exports.examples = [
|
|||
title: 'TextInput Intrinsic Size',
|
||||
render: function() {
|
||||
return (
|
||||
<View style={{height: 80}}>
|
||||
<TextInput
|
||||
style={{
|
||||
position: 'absolute',
|
||||
fontSize: 16,
|
||||
backgroundColor: '#eeeeee',
|
||||
borderWidth: 5,
|
||||
padding: 10,
|
||||
paddingTop: 20,
|
||||
}}
|
||||
placeholder="Placeholder defines intrinsic size"
|
||||
/>
|
||||
<View>
|
||||
<Text>Singleline TextInput</Text>
|
||||
<View style={{height: 80}}>
|
||||
<TextInput
|
||||
style={{
|
||||
position: 'absolute',
|
||||
fontSize: 16,
|
||||
backgroundColor: '#eeeeee',
|
||||
borderColor: '#666666',
|
||||
borderWidth: 5,
|
||||
padding: 10,
|
||||
paddingTop: 20,
|
||||
}}
|
||||
placeholder="Placeholder defines intrinsic size"
|
||||
/>
|
||||
</View>
|
||||
<Text>Multiline TextInput</Text>
|
||||
<View style={{height: 80}}>
|
||||
<TextInput
|
||||
style={{
|
||||
position: 'absolute',
|
||||
fontSize: 16,
|
||||
backgroundColor: '#eeeeee',
|
||||
borderColor: '#666666',
|
||||
borderWidth: 5,
|
||||
padding: 10,
|
||||
paddingTop: 20,
|
||||
borderTopWidth: 20,
|
||||
}}
|
||||
multiline={true}
|
||||
placeholder="Placeholder defines intrinsic size"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue