diff --git a/Examples/UIExplorer/TextInputExample.ios.js b/Examples/UIExplorer/TextInputExample.ios.js index d51a95e33bb5fd..c4f0bdafd2b29c 100644 --- a/Examples/UIExplorer/TextInputExample.ios.js +++ b/Examples/UIExplorer/TextInputExample.ios.js @@ -149,6 +149,15 @@ var styles = StyleSheet.create({ right: 5, backgroundColor: 'red', }, + multilineAutoGrow: { + borderWidth: 0.5, + borderColor: '#0f0f0f', + flex: 1, + fontSize: 13, + height: 30, + padding: 4, + marginBottom: 4, + }, eventLabel: { margin: 3, fontSize: 12, @@ -434,6 +443,13 @@ exports.examples = [ style={styles.multiline}> + + ); } diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index f96c1852fca60a..b19886bf2a3fce 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -34,6 +34,8 @@ var onlyMultiline = { onSelectionChange: true, // not supported in Open Source yet onTextInput: true, // not supported in Open Source yet children: true, + autoGrow: true, + maxHeight: true, }; var notMultiline = { @@ -100,6 +102,16 @@ var TextInput = React.createClass({ * If false, disables auto-correct. The default value is true. */ autoCorrect: PropTypes.bool, + /** + * If true, and the input is multiline, the input's height will grow automatically. The default value is false. + * @platorm ios + */ + autoGrow: PropTypes.bool, + /** + * The maximum height the input should grow to when autoGrow is true. + * @platorm ios + */ + maxHeight: PropTypes.number, /** * If true, focuses the input on componentDidMount. * The default value is false. diff --git a/Libraries/Text/RCTTextView.h b/Libraries/Text/RCTTextView.h index c5012ec0917411..4df27d37bf6d58 100644 --- a/Libraries/Text/RCTTextView.h +++ b/Libraries/Text/RCTTextView.h @@ -10,6 +10,7 @@ #import #import "RCTView.h" +#import "RCTUIManager.h" #import "UIView+React.h" @class RCTEventDispatcher; @@ -17,6 +18,8 @@ @interface RCTTextView : RCTView @property (nonatomic, assign) BOOL autoCorrect; +@property (nonatomic, assign) BOOL autoGrow; +@property (nonatomic, assign) float maxHeight; @property (nonatomic, assign) BOOL clearTextOnFocus; @property (nonatomic, assign) BOOL selectTextOnFocus; @property (nonatomic, assign) UIEdgeInsets contentInset; @@ -28,6 +31,6 @@ @property (nonatomic, assign) NSInteger mostRecentEventCount; @property (nonatomic, strong) NSNumber *maxLength; -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER; @end diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index d21360ed26c1be..74435ffed9d8b1 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -12,25 +12,32 @@ #import "RCTConvert.h" #import "RCTEventDispatcher.h" #import "RCTUtils.h" +#import "RCTUIManager.h" #import "UIView+React.h" @implementation RCTTextView { + RCTBridge *_bridge; RCTEventDispatcher *_eventDispatcher; BOOL _jsRequestingFirstResponder; + BOOL _autoGrow; + float _origHeight; + float _maxHeight; NSString *_placeholder; UITextView *_placeholderView; UITextView *_textView; NSInteger _nativeEventCount; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithBridge:(RCTBridge *)bridge { - RCTAssertParam(eventDispatcher); + RCTAssertParam(bridge); if ((self = [super initWithFrame:CGRectZero])) { + _bridge = bridge; + _autoGrow = false; _contentInset = UIEdgeInsetsZero; - _eventDispatcher = eventDispatcher; + _eventDispatcher = _bridge.eventDispatcher; _placeholderTextColor = [self defaultPlaceholderTextColor]; _textView = [[UITextView alloc] initWithFrame:self.bounds]; @@ -67,6 +74,10 @@ - (void)updateFrames _textView.textContainerInset = adjustedTextContainerInset; _placeholderView.textContainerInset = adjustedTextContainerInset; + + if (! _origHeight) { + _origHeight = self.frame.size.height; + } } - (void)updatePlaceholder @@ -89,6 +100,40 @@ - (void)updatePlaceholder } } +- (void)updateTextViewFrame +{ + if (self.superview == nil) { + return; + } + + if (_autoGrow) { + if (CGRectIsEmpty(self.frame)) { + return; + } + + float currentHeight = _textView.frame.size.height; + float newHeight; + + [_textView sizeToFit]; + + if (_textView.frame.size.height >= _origHeight) { + newHeight = _textView.frame.size.height; + } else { + newHeight = _origHeight; + } + + if (_maxHeight > _origHeight) { + newHeight = fminf(newHeight, _maxHeight); + } + + if (newHeight != currentHeight) { + CGRect newFrame = CGRectMake(0, 0, self.frame.size.width, newHeight); + [_bridge.uiManager setFrame:newFrame + forView:self]; + } + } +} + - (UIFont *)font { return _textView.font; @@ -98,6 +143,7 @@ - (void)setFont:(UIFont *)font { _textView.font = font; [self updatePlaceholder]; + [self updateTextViewFrame]; } - (UIColor *)textColor @@ -169,6 +215,7 @@ - (void)setText:(NSString *)text UITextRange *selection = _textView.selectedTextRange; _textView.text = text; [self _setPlaceholderVisibility]; + [self updateTextViewFrame]; _textView.selectedTextRange = selection; // maintain cursor position/selection - this is robust to out of bounds } else if (eventLag > RCTTextUpdateLagWarningThreshold) { RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag); @@ -194,6 +241,16 @@ - (BOOL)autoCorrect return _textView.autocorrectionType == UITextAutocorrectionTypeYes; } +- (void)setAutoGrow:(BOOL)autoGrow +{ + _autoGrow = autoGrow; +} + +- (void)setMaxHeight:(float)maxHeight +{ + _maxHeight = maxHeight; +} + - (BOOL)textViewShouldBeginEditing:(UITextView *)textView { if (_selectTextOnFocus) { @@ -220,6 +277,8 @@ - (void)textViewDidBeginEditing:(UITextView *)textView - (void)textViewDidChange:(UITextView *)textView { [self _setPlaceholderVisibility]; + [self updateTextViewFrame]; + _nativeEventCount++; [_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange reactTag:self.reactTag diff --git a/Libraries/Text/RCTTextViewManager.m b/Libraries/Text/RCTTextViewManager.m index f47a106bdf49a9..7b4851ce9b6ac7 100644 --- a/Libraries/Text/RCTTextViewManager.m +++ b/Libraries/Text/RCTTextViewManager.m @@ -21,10 +21,12 @@ @implementation RCTTextViewManager - (UIView *)view { - return [[RCTTextView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [[RCTTextView alloc] initWithBridge:self.bridge]; } RCT_EXPORT_VIEW_PROPERTY(autoCorrect, BOOL) +RCT_EXPORT_VIEW_PROPERTY(autoGrow, BOOL) +RCT_EXPORT_VIEW_PROPERTY(maxHeight, float) RCT_REMAP_VIEW_PROPERTY(editable, textView.editable, BOOL) RCT_EXPORT_VIEW_PROPERTY(placeholder, NSString) RCT_EXPORT_VIEW_PROPERTY(placeholderTextColor, UIColor)